From 5d6ade551eda23058b1ea8fa8430ef4978a12eeb Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Thu, 3 Dec 2020 11:56:16 +0100 Subject: [PATCH 1/2] Add diesel to the rustc perf test suite As far as I know diesel is a rather strange workload for rustc. According to some short measurements most of the time compiling diesel is spend type checking the crate and resolving trait bounds. I see multiple reasons for this: * Diesel builts a complex abstract query dsl for SQL based on rust generics. All fo this needs to be type checked. * Diesel generates a ton of trait impls for tuples of various sizes. There are features to set the supported max size to 16, 32, 64 and 128 tuple elements. As this is a benchmark, I've chosen to set it to 128 to maximize the number of impls. As consequence of this diesels compile times are quite sensitive to changes touching the type system in general and the trait resolution in detail. Any change that will introduce a behaviour which does not scale well with the number of available trait impls will likely result in a huge increase for this benchmark. --- collector/benchmarks/README.md | 4 + collector/benchmarks/diesel/.editorconfig | 29 + collector/benchmarks/diesel/.env.sample | 15 + .../benchmarks/diesel/.github/FUNDING.yml | 1 + .../diesel/.github/ISSUE_TEMPLATE.md | 51 + .../diesel/.github/workflows/audit.yml | 16 + .../diesel/.github/workflows/benches.yml | 53 + .../diesel/.github/workflows/ci.yml | 426 + .../diesel/.github/workflows/doc.yml | 54 + collector/benchmarks/diesel/.gitignore | 4 + collector/benchmarks/diesel/CHANGELOG.md | 1867 ++++ collector/benchmarks/diesel/CONTRIBUTING.md | 177 + collector/benchmarks/diesel/Cargo.lock | 590 ++ collector/benchmarks/diesel/Cargo.toml | 5 + collector/benchmarks/diesel/LICENSE-APACHE | 190 + collector/benchmarks/diesel/LICENSE-MIT | 21 + collector/benchmarks/diesel/README.md | 62 + collector/benchmarks/diesel/clippy.toml | 10 + .../benchmarks/diesel/code_of_conduct.md | 80 + collector/benchmarks/diesel/diesel/Cargo.toml | 65 + .../benchmarks/diesel/diesel/LICENSE-APACHE | 1 + .../benchmarks/diesel/diesel/LICENSE-MIT | 1 + collector/benchmarks/diesel/diesel/README.md | 9 + .../diesel/src/associations/belongs_to.rs | 178 + .../diesel/diesel/src/associations/mod.rs | 413 + .../benchmarks/diesel/diesel/src/backend.rs | 82 + .../diesel/diesel/src/connection/mod.rs | 186 + .../diesel/src/connection/statement_cache.rs | 256 + .../src/connection/transaction_manager.rs | 247 + .../diesel/diesel/src/data_types.rs | 7 + .../diesel/diesel/src/deserialize.rs | 442 + .../diesel/diesel/src/doctest_setup.rs | 232 + .../diesel/src/expression/array_comparison.rs | 199 + .../diesel/diesel/src/expression/bound.rs | 55 + .../diesel/diesel/src/expression/coerce.rs | 73 + .../diesel/diesel/src/expression/count.rs | 73 + .../diesel/diesel/src/expression/exists.rs | 92 + .../expression/functions/aggregate_folding.rs | 68 + .../functions/aggregate_ordering.rs | 52 + .../src/expression/functions/date_and_time.rs | 87 + .../src/expression/functions/helper_types.rs | 21 + .../diesel/src/expression/functions/mod.rs | 103 + .../diesel/diesel/src/expression/grouped.rs | 23 + .../diesel/src/expression/helper_types.rs | 120 + .../diesel/diesel/src/expression/mod.rs | 763 ++ .../diesel/diesel/src/expression/not.rs | 31 + .../diesel/diesel/src/expression/nullable.rs | 60 + .../diesel/diesel/src/expression/operators.rs | 586 ++ .../diesel/diesel/src/expression/ops/mod.rs | 31 + .../diesel/src/expression/ops/numeric.rs | 58 + .../diesel/src/expression/sql_literal.rs | 348 + .../diesel/diesel/src/expression/subselect.rs | 69 + .../bool_expression_methods.rs | 106 + .../diesel/src/expression_methods/eq_all.rs | 73 + .../escape_expression_methods.rs | 62 + .../global_expression_methods.rs | 479 + .../diesel/src/expression_methods/mod.rs | 26 + .../text_expression_methods.rs | 154 + .../diesel/diesel/src/insertable.rs | 334 + collector/benchmarks/diesel/diesel/src/lib.rs | 368 + .../diesel/diesel/src/macros/internal.rs | 36 + .../diesel/diesel/src/macros/mod.rs | 1509 +++ .../diesel/diesel/src/macros/ops.rs | 106 + .../diesel/diesel/src/macros/static_cond.rs | 38 + .../diesel/diesel/src/macros/tuples.rs | 8532 +++++++++++++++++ .../diesel/diesel/src/migration/errors.rs | 121 + .../diesel/diesel/src/migration/mod.rs | 102 + .../src/migration/setup_migration_table.sql | 4 + .../diesel/diesel/src/mysql/backend.rs | 81 + .../diesel/src/mysql/connection/bind.rs | 1438 +++ .../diesel/diesel/src/mysql/connection/mod.rs | 162 + .../diesel/diesel/src/mysql/connection/raw.rs | 218 + .../src/mysql/connection/stmt/iterator.rs | 122 + .../src/mysql/connection/stmt/metadata.rs | 60 + .../diesel/src/mysql/connection/stmt/mod.rs | 180 + .../diesel/diesel/src/mysql/connection/url.rs | 229 + .../benchmarks/diesel/diesel/src/mysql/mod.rs | 17 + .../src/mysql/query_builder/limit_offset.rs | 104 + .../diesel/src/mysql/query_builder/mod.rs | 41 + .../query_builder/query_fragment_impls.rs | 38 + .../diesel/src/mysql/types/date_and_time.rs | 281 + .../diesel/diesel/src/mysql/types/json.rs | 55 + .../diesel/diesel/src/mysql/types/mod.rs | 195 + .../diesel/diesel/src/mysql/types/numeric.rs | 39 + .../diesel/src/mysql/types/primitives.rs | 121 + .../diesel/diesel/src/mysql/value.rs | 101 + .../diesel/diesel/src/pg/backend.rs | 50 + .../diesel/diesel/src/pg/connection/cursor.rs | 48 + .../diesel/diesel/src/pg/connection/mod.rs | 255 + .../diesel/diesel/src/pg/connection/raw.rs | 158 + .../diesel/diesel/src/pg/connection/result.rs | 208 + .../diesel/diesel/src/pg/connection/row.rs | 75 + .../diesel/src/pg/connection/stmt/mod.rs | 83 + .../diesel/diesel/src/pg/expression/array.rs | 98 + .../src/pg/expression/array_comparison.rs | 169 + .../diesel/src/pg/expression/date_and_time.rs | 50 + .../src/pg/expression/expression_methods.rs | 1113 +++ .../pg/expression/extensions/interval_dsl.rs | 320 + .../src/pg/expression/extensions/mod.rs | 6 + .../diesel/src/pg/expression/functions.rs | 53 + .../diesel/src/pg/expression/helper_types.rs | 80 + .../diesel/diesel/src/pg/expression/mod.rs | 36 + .../diesel/src/pg/expression/operators.rs | 22 + .../diesel/diesel/src/pg/metadata_lookup.rs | 73 + .../benchmarks/diesel/diesel/src/pg/mod.rs | 42 + .../src/pg/query_builder/distinct_on.rs | 44 + .../src/pg/query_builder/limit_offset.rs | 43 + .../diesel/diesel/src/pg/query_builder/mod.rs | 73 + .../src/pg/query_builder/on_constraint.rs | 62 + .../pg/query_builder/query_fragment_impls.rs | 54 + .../diesel/diesel/src/pg/serialize/mod.rs | 3 + .../diesel/src/pg/serialize/write_tuple.rs | 47 + .../diesel/diesel/src/pg/transaction.rs | 401 + .../diesel/diesel/src/pg/types/array.rs | 143 + .../src/pg/types/date_and_time/chrono.rs | 312 + .../diesel/src/pg/types/date_and_time/mod.rs | 161 + .../types/date_and_time/quickcheck_impls.rs | 37 + .../src/pg/types/date_and_time/std_time.rs | 107 + .../diesel/diesel/src/pg/types/floats/mod.rs | 113 + .../src/pg/types/floats/quickcheck_impls.rs | 69 + .../diesel/diesel/src/pg/types/integers.rs | 54 + .../diesel/diesel/src/pg/types/json.rs | 119 + .../diesel/diesel/src/pg/types/mac_addr.rs | 45 + .../diesel/diesel/src/pg/types/mod.rs | 512 + .../diesel/diesel/src/pg/types/money.rs | 159 + .../diesel/src/pg/types/network_address.rs | 260 + .../diesel/diesel/src/pg/types/numeric.rs | 330 + .../diesel/diesel/src/pg/types/primitives.rs | 37 + .../diesel/diesel/src/pg/types/ranges.rs | 213 + .../diesel/diesel/src/pg/types/record.rs | 241 + .../diesel/diesel/src/pg/types/uuid.rs | 61 + .../benchmarks/diesel/diesel/src/pg/value.rs | 52 + .../diesel/src/query_builder/ast_pass.rs | 251 + .../src/query_builder/bind_collector.rs | 79 + .../diesel/src/query_builder/clause_macro.rs | 57 + .../diesel/src/query_builder/debug_query.rs | 98 + .../src/query_builder/delete_statement/mod.rs | 233 + .../src/query_builder/distinct_clause.rs | 24 + .../diesel/src/query_builder/functions.rs | 454 + .../src/query_builder/group_by_clause.rs | 13 + .../insert_statement/column_list.rs | 29 + .../insert_statement/insert_from_select.rs | 66 + .../src/query_builder/insert_statement/mod.rs | 619 ++ .../diesel/src/query_builder/limit_clause.rs | 11 + .../src/query_builder/limit_offset_clause.rs | 24 + .../src/query_builder/locking_clause.rs | 61 + .../diesel/diesel/src/query_builder/mod.rs | 336 + .../diesel/src/query_builder/nodes/mod.rs | 43 + .../diesel/src/query_builder/offset_clause.rs | 11 + .../diesel/src/query_builder/order_clause.rs | 20 + .../diesel/src/query_builder/query_id.rs | 144 + .../src/query_builder/returning_clause.rs | 8 + .../diesel/src/query_builder/select_clause.rs | 110 + .../query_builder/select_statement/boxed.rs | 392 + .../select_statement/dsl_impls.rs | 487 + .../src/query_builder/select_statement/mod.rs | 215 + .../diesel/src/query_builder/sql_query.rs | 256 + .../update_statement/changeset.rs | 82 + .../src/query_builder/update_statement/mod.rs | 288 + .../query_builder/update_statement/target.rs | 47 + .../upsert/into_conflict_clause.rs | 124 + .../diesel/src/query_builder/upsert/mod.rs | 5 + .../upsert/on_conflict_actions.rs | 84 + .../upsert/on_conflict_clause.rs | 57 + .../upsert/on_conflict_target.rs | 106 + .../upsert/on_conflict_target_decorations.rs | 71 + .../diesel/src/query_builder/where_clause.rs | 190 + .../diesel/src/query_dsl/belonging_to_dsl.rs | 54 + .../diesel/diesel/src/query_dsl/boxed_dsl.rs | 36 + .../diesel/src/query_dsl/distinct_dsl.rs | 68 + .../diesel/diesel/src/query_dsl/filter_dsl.rs | 85 + .../diesel/src/query_dsl/group_by_dsl.rs | 35 + .../diesel/diesel/src/query_dsl/join_dsl.rs | 80 + .../diesel/diesel/src/query_dsl/limit_dsl.rs | 28 + .../diesel/diesel/src/query_dsl/load_dsl.rs | 57 + .../diesel/src/query_dsl/locking_dsl.rs | 55 + .../diesel/diesel/src/query_dsl/mod.rs | 1354 +++ .../src/query_dsl/nullable_select_dsl.rs | 14 + .../diesel/diesel/src/query_dsl/offset_dsl.rs | 28 + .../diesel/diesel/src/query_dsl/order_dsl.rs | 58 + .../diesel/src/query_dsl/save_changes_dsl.rs | 155 + .../diesel/diesel/src/query_dsl/select_dsl.rs | 33 + .../diesel/src/query_dsl/single_value_dsl.rs | 34 + .../diesel/diesel/src/query_source/joins.rs | 341 + .../diesel/diesel/src/query_source/mod.rs | 158 + .../diesel/src/query_source/peano_numbers.rs | 45 + .../benchmarks/diesel/diesel/src/r2d2.rs | 303 + .../benchmarks/diesel/diesel/src/result.rs | 369 + collector/benchmarks/diesel/diesel/src/row.rs | 205 + .../benchmarks/diesel/diesel/src/serialize.rs | 205 + .../diesel/diesel/src/sql_types/fold.rs | 47 + .../diesel/diesel/src/sql_types/mod.rs | 632 ++ .../diesel/diesel/src/sql_types/ops.rs | 184 + .../diesel/diesel/src/sql_types/ord.rs | 30 + .../diesel/diesel/src/sqlite/backend.rs | 57 + .../connection/diesel_manage_updated_at.sql | 11 + .../diesel/src/sqlite/connection/functions.rs | 170 + .../diesel/src/sqlite/connection/mod.rs | 517 + .../diesel/src/sqlite/connection/raw.rs | 390 + .../src/sqlite/connection/serialized_value.rs | 113 + .../src/sqlite/connection/sqlite_value.rs | 189 + .../sqlite/connection/statement_iterator.rs | 36 + .../diesel/src/sqlite/connection/stmt.rs | 154 + .../diesel/diesel/src/sqlite/mod.rs | 35 + .../src/sqlite/query_builder/limit_offset.rs | 126 + .../diesel/src/sqlite/query_builder/mod.rs | 42 + .../src/sqlite/types/date_and_time/chrono.rs | 378 + .../src/sqlite/types/date_and_time/mod.rs | 79 + .../diesel/diesel/src/sqlite/types/mod.rs | 77 + .../diesel/diesel/src/sqlite/types/numeric.rs | 15 + .../diesel/diesel/src/test_helpers.rs | 71 + .../diesel/src/type_impls/date_and_time.rs | 41 + .../diesel/diesel/src/type_impls/decimal.rs | 15 + .../diesel/diesel/src/type_impls/floats.rs | 58 + .../diesel/diesel/src/type_impls/integers.rs | 99 + .../diesel/diesel/src/type_impls/json.rs | 13 + .../diesel/diesel/src/type_impls/mod.rs | 9 + .../diesel/diesel/src/type_impls/option.rs | 136 + .../diesel/src/type_impls/primitives.rs | 277 + .../diesel/diesel/src/type_impls/tuples.rs | 502 + .../diesel/diesel/src/upsert/mod.rs | 27 + .../src/upsert/on_conflict_docs_setup.rs | 9 + .../src/upsert/on_conflict_extension.rs | 391 + .../benchmarks/diesel/diesel/src/util.rs | 11 + .../diesel/diesel_derives/Cargo.toml | 37 + .../diesel/diesel_derives/LICENSE-APACHE | 1 + .../diesel/diesel_derives/LICENSE-MIT | 1 + .../diesel/diesel_derives/src/as_changeset.rs | 122 + .../diesel_derives/src/as_expression.rs | 115 + .../diesel/diesel_derives/src/associations.rs | 206 + .../diesel_derives/src/diagnostic_shim.rs | 87 + .../diesel_derives/src/diesel_numeric_ops.rs | 83 + .../diesel/diesel_derives/src/field.rs | 125 + .../diesel/diesel_derives/src/from_sql_row.rs | 44 + .../diesel/diesel_derives/src/identifiable.rs | 47 + .../diesel/diesel_derives/src/insertable.rs | 183 + .../diesel/diesel_derives/src/lib.rs | 1175 +++ .../diesel/diesel_derives/src/meta.rs | 272 + .../diesel/diesel_derives/src/model.rs | 112 + .../diesel/diesel_derives/src/query_id.rs | 37 + .../diesel/diesel_derives/src/queryable.rs | 63 + .../diesel_derives/src/queryable_by_name.rs | 120 + .../diesel_derives/src/resolved_at_shim.rs | 19 + .../diesel/diesel_derives/src/sql_function.rs | 384 + .../diesel/diesel_derives/src/sql_type.rs | 159 + .../diesel/diesel_derives/src/util.rs | 85 + .../diesel_derives/src/valid_grouping.rs | 69 + .../diesel_derives/tests/as_changeset.rs | 449 + .../diesel_derives/tests/associations.rs | 293 + .../diesel/diesel_derives/tests/helpers.rs | 80 + .../diesel_derives/tests/identifiable.rs | 152 + .../diesel/diesel_derives/tests/insertable.rs | 298 + .../diesel/diesel_derives/tests/queryable.rs | 38 + .../diesel_derives/tests/queryable_by_name.rs | 109 + .../diesel/diesel_derives/tests/schema.rs | 7 + .../diesel/diesel_derives/tests/tests.rs | 16 + .../20170207193805_initial_schema/down.sql | 6 + .../20170207193805_initial_schema/up.sql | 36 + .../down.sql | 5 + .../up.sql | 5 + .../down.sql | 2 + .../up.sql | 2 + .../20170215170122_create_trees/down.sql | 1 + .../mysql/20170215170122_create_trees/up.sql | 4 + .../20170219200159_add_foreign_key/down.sql | 2 + .../20170219200159_add_foreign_key/up.sql | 9 + .../down.sql | 1 + .../20170407152306_add_nullable_table/up.sql | 4 + .../mysql/20170603131224_add_likes/down.sql | 1 + .../mysql/20170603131224_add_likes/up.sql | 5 + .../20170804172328_add_foreign_keys/down.sql | 6 + .../20170804172328_add_foreign_keys/up.sql | 6 + .../down.sql | 6 + .../up.sql | 36 + .../20170811104602_add_blob_types/down.sql | 1 + .../20170811104602_add_blob_types/up.sql | 7 + .../down.sql | 1 + .../20170816164349_add_keywords_table/up.sql | 5 + .../down.sql | 1 + .../up.sql | 4 + .../2020-02-18-111430_create_pokes/down.sql | 1 + .../2020-02-18-111430_create_pokes/up.sql | 5 + .../down.sql | 2 + .../up.sql | 36 + .../down.sql | 3 + .../up.sql | 18 + .../20160107090901_add_tags_to_posts/down.sql | 1 + .../20160107090901_add_tags_to_posts/up.sql | 1 + .../down.sql | 2 + .../up.sql | 10 + .../20160825135747_create_followings/down.sql | 1 + .../20160825135747_create_followings/up.sql | 6 + .../down.sql | 1 + .../up.sql | 2 + .../2017-09-26-151545_fix_posts_tags/down.sql | 1 + .../2017-09-26-151545_fix_posts_tags/up.sql | 1 + .../down.sql | 2 + .../up.sql | 1 + .../down.sql | 5 + .../up.sql | 5 + .../down.sql | 2 + .../up.sql | 2 + .../20170215170122_create_trees/down.sql | 1 + .../20170215170122_create_trees/up.sql | 4 + .../20170219200159_add_foreign_key/down.sql | 2 + .../20170219200159_add_foreign_key/up.sql | 8 + .../down.sql | 1 + .../20170407152306_add_nullable_table/up.sql | 4 + .../20170603131224_add_likes/down.sql | 1 + .../20170603131224_add_likes/up.sql | 5 + .../20170717152137_add_ranges/down.sql | 1 + .../20170717152137_add_ranges/up.sql | 8 + .../down.sql | 1 + .../20170721184144_create_citext_table/up.sql | 2 + .../20170804172328_add_foreign_keys/down.sql | 6 + .../20170804172328_add_foreign_keys/up.sql | 6 + .../down.sql | 7 + .../up.sql | 37 + .../down.sql | 1 + .../20170816164352_add_keywords_table/up.sql | 5 + .../2020-02-18-111430_create_pokes/down.sql | 1 + .../2020-02-18-111430_create_pokes/up.sql | 5 + .../down.sql | 3 + .../up.sql | 18 + .../down.sql | 2 + .../up.sql | 10 + .../down.sql | 4 + .../20160416143735_infer_all_the_types/up.sql | 46 + .../20160825135747_create_followings/down.sql | 1 + .../20160825135747_create_followings/up.sql | 6 + .../down.sql | 5 + .../up.sql | 5 + .../down.sql | 2 + .../up.sql | 2 + .../20170215170122_create_trees/down.sql | 1 + .../sqlite/20170215170122_create_trees/up.sql | 4 + .../20170219200159_add_foreign_key/down.sql | 2 + .../20170219200159_add_foreign_key/up.sql | 9 + .../down.sql | 1 + .../20170407152306_add_nullable_table/up.sql | 4 + .../down.sql | 1 + .../up.sql | 6 + .../sqlite/20170603131224_add_likes/down.sql | 1 + .../sqlite/20170603131224_add_likes/up.sql | 5 + .../20170804172328_add_foreign_keys/down.sql | 33 + .../20170804172328_add_foreign_keys/up.sql | 39 + .../down.sql | 7 + .../up.sql | 39 + .../down.sql | 1 + .../20170816164356_add_keywords_table/up.sql | 5 + .../2020-02-18-111430_create_pokes/down.sql | 1 + .../2020-02-18-111430_create_pokes/up.sql | 5 + collector/benchmarks/diesel/perf-config.json | 4 + 353 files changed, 50375 insertions(+) create mode 100644 collector/benchmarks/diesel/.editorconfig create mode 100644 collector/benchmarks/diesel/.env.sample create mode 100644 collector/benchmarks/diesel/.github/FUNDING.yml create mode 100644 collector/benchmarks/diesel/.github/ISSUE_TEMPLATE.md create mode 100644 collector/benchmarks/diesel/.github/workflows/audit.yml create mode 100644 collector/benchmarks/diesel/.github/workflows/benches.yml create mode 100644 collector/benchmarks/diesel/.github/workflows/ci.yml create mode 100644 collector/benchmarks/diesel/.github/workflows/doc.yml create mode 100644 collector/benchmarks/diesel/.gitignore create mode 100644 collector/benchmarks/diesel/CHANGELOG.md create mode 100644 collector/benchmarks/diesel/CONTRIBUTING.md create mode 100644 collector/benchmarks/diesel/Cargo.lock create mode 100644 collector/benchmarks/diesel/Cargo.toml create mode 100644 collector/benchmarks/diesel/LICENSE-APACHE create mode 100644 collector/benchmarks/diesel/LICENSE-MIT create mode 100644 collector/benchmarks/diesel/README.md create mode 100644 collector/benchmarks/diesel/clippy.toml create mode 100644 collector/benchmarks/diesel/code_of_conduct.md create mode 100644 collector/benchmarks/diesel/diesel/Cargo.toml create mode 120000 collector/benchmarks/diesel/diesel/LICENSE-APACHE create mode 120000 collector/benchmarks/diesel/diesel/LICENSE-MIT create mode 100644 collector/benchmarks/diesel/diesel/README.md create mode 100644 collector/benchmarks/diesel/diesel/src/associations/belongs_to.rs create mode 100644 collector/benchmarks/diesel/diesel/src/associations/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/backend.rs create mode 100644 collector/benchmarks/diesel/diesel/src/connection/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/connection/statement_cache.rs create mode 100644 collector/benchmarks/diesel/diesel/src/connection/transaction_manager.rs create mode 100644 collector/benchmarks/diesel/diesel/src/data_types.rs create mode 100644 collector/benchmarks/diesel/diesel/src/deserialize.rs create mode 100644 collector/benchmarks/diesel/diesel/src/doctest_setup.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/array_comparison.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/bound.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/coerce.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/count.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/exists.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/functions/aggregate_folding.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/functions/aggregate_ordering.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/functions/date_and_time.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/functions/helper_types.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/functions/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/grouped.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/helper_types.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/not.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/nullable.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/operators.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/ops/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/ops/numeric.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/sql_literal.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression/subselect.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression_methods/bool_expression_methods.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression_methods/eq_all.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression_methods/escape_expression_methods.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression_methods/global_expression_methods.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression_methods/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/expression_methods/text_expression_methods.rs create mode 100644 collector/benchmarks/diesel/diesel/src/insertable.rs create mode 100644 collector/benchmarks/diesel/diesel/src/lib.rs create mode 100644 collector/benchmarks/diesel/diesel/src/macros/internal.rs create mode 100644 collector/benchmarks/diesel/diesel/src/macros/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/macros/ops.rs create mode 100644 collector/benchmarks/diesel/diesel/src/macros/static_cond.rs create mode 100644 collector/benchmarks/diesel/diesel/src/macros/tuples.rs create mode 100644 collector/benchmarks/diesel/diesel/src/migration/errors.rs create mode 100644 collector/benchmarks/diesel/diesel/src/migration/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/migration/setup_migration_table.sql create mode 100644 collector/benchmarks/diesel/diesel/src/mysql/backend.rs create mode 100644 collector/benchmarks/diesel/diesel/src/mysql/connection/bind.rs create mode 100644 collector/benchmarks/diesel/diesel/src/mysql/connection/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/mysql/connection/raw.rs create mode 100644 collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/iterator.rs create mode 100644 collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/metadata.rs create mode 100644 collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/mysql/connection/url.rs create mode 100644 collector/benchmarks/diesel/diesel/src/mysql/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/mysql/query_builder/limit_offset.rs create mode 100644 collector/benchmarks/diesel/diesel/src/mysql/query_builder/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/mysql/query_builder/query_fragment_impls.rs create mode 100644 collector/benchmarks/diesel/diesel/src/mysql/types/date_and_time.rs create mode 100644 collector/benchmarks/diesel/diesel/src/mysql/types/json.rs create mode 100644 collector/benchmarks/diesel/diesel/src/mysql/types/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/mysql/types/numeric.rs create mode 100644 collector/benchmarks/diesel/diesel/src/mysql/types/primitives.rs create mode 100644 collector/benchmarks/diesel/diesel/src/mysql/value.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/backend.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/connection/cursor.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/connection/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/connection/raw.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/connection/result.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/connection/row.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/connection/stmt/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/expression/array.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/expression/array_comparison.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/expression/date_and_time.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/expression/expression_methods.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/expression/extensions/interval_dsl.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/expression/extensions/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/expression/functions.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/expression/helper_types.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/expression/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/expression/operators.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/metadata_lookup.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/query_builder/distinct_on.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/query_builder/limit_offset.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/query_builder/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/query_builder/on_constraint.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/query_builder/query_fragment_impls.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/serialize/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/serialize/write_tuple.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/transaction.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/types/array.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/types/date_and_time/chrono.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/types/date_and_time/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/types/date_and_time/quickcheck_impls.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/types/date_and_time/std_time.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/types/floats/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/types/floats/quickcheck_impls.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/types/integers.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/types/json.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/types/mac_addr.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/types/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/types/money.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/types/network_address.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/types/numeric.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/types/primitives.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/types/ranges.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/types/record.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/types/uuid.rs create mode 100644 collector/benchmarks/diesel/diesel/src/pg/value.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/ast_pass.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/bind_collector.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/clause_macro.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/debug_query.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/delete_statement/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/distinct_clause.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/functions.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/group_by_clause.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/insert_statement/column_list.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/insert_statement/insert_from_select.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/insert_statement/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/limit_clause.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/limit_offset_clause.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/locking_clause.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/nodes/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/offset_clause.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/order_clause.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/query_id.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/returning_clause.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/select_clause.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/select_statement/boxed.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/select_statement/dsl_impls.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/select_statement/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/sql_query.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/update_statement/changeset.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/update_statement/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/update_statement/target.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/upsert/into_conflict_clause.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/upsert/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_actions.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_clause.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_target.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_target_decorations.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_builder/where_clause.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_dsl/belonging_to_dsl.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_dsl/boxed_dsl.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_dsl/distinct_dsl.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_dsl/filter_dsl.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_dsl/group_by_dsl.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_dsl/join_dsl.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_dsl/limit_dsl.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_dsl/load_dsl.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_dsl/locking_dsl.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_dsl/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_dsl/nullable_select_dsl.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_dsl/offset_dsl.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_dsl/order_dsl.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_dsl/save_changes_dsl.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_dsl/select_dsl.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_dsl/single_value_dsl.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_source/joins.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_source/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/query_source/peano_numbers.rs create mode 100644 collector/benchmarks/diesel/diesel/src/r2d2.rs create mode 100644 collector/benchmarks/diesel/diesel/src/result.rs create mode 100644 collector/benchmarks/diesel/diesel/src/row.rs create mode 100644 collector/benchmarks/diesel/diesel/src/serialize.rs create mode 100644 collector/benchmarks/diesel/diesel/src/sql_types/fold.rs create mode 100644 collector/benchmarks/diesel/diesel/src/sql_types/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/sql_types/ops.rs create mode 100644 collector/benchmarks/diesel/diesel/src/sql_types/ord.rs create mode 100644 collector/benchmarks/diesel/diesel/src/sqlite/backend.rs create mode 100644 collector/benchmarks/diesel/diesel/src/sqlite/connection/diesel_manage_updated_at.sql create mode 100644 collector/benchmarks/diesel/diesel/src/sqlite/connection/functions.rs create mode 100644 collector/benchmarks/diesel/diesel/src/sqlite/connection/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/sqlite/connection/raw.rs create mode 100644 collector/benchmarks/diesel/diesel/src/sqlite/connection/serialized_value.rs create mode 100644 collector/benchmarks/diesel/diesel/src/sqlite/connection/sqlite_value.rs create mode 100644 collector/benchmarks/diesel/diesel/src/sqlite/connection/statement_iterator.rs create mode 100644 collector/benchmarks/diesel/diesel/src/sqlite/connection/stmt.rs create mode 100644 collector/benchmarks/diesel/diesel/src/sqlite/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/sqlite/query_builder/limit_offset.rs create mode 100644 collector/benchmarks/diesel/diesel/src/sqlite/query_builder/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/sqlite/types/date_and_time/chrono.rs create mode 100644 collector/benchmarks/diesel/diesel/src/sqlite/types/date_and_time/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/sqlite/types/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/sqlite/types/numeric.rs create mode 100644 collector/benchmarks/diesel/diesel/src/test_helpers.rs create mode 100644 collector/benchmarks/diesel/diesel/src/type_impls/date_and_time.rs create mode 100644 collector/benchmarks/diesel/diesel/src/type_impls/decimal.rs create mode 100644 collector/benchmarks/diesel/diesel/src/type_impls/floats.rs create mode 100644 collector/benchmarks/diesel/diesel/src/type_impls/integers.rs create mode 100644 collector/benchmarks/diesel/diesel/src/type_impls/json.rs create mode 100644 collector/benchmarks/diesel/diesel/src/type_impls/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/type_impls/option.rs create mode 100644 collector/benchmarks/diesel/diesel/src/type_impls/primitives.rs create mode 100644 collector/benchmarks/diesel/diesel/src/type_impls/tuples.rs create mode 100644 collector/benchmarks/diesel/diesel/src/upsert/mod.rs create mode 100644 collector/benchmarks/diesel/diesel/src/upsert/on_conflict_docs_setup.rs create mode 100644 collector/benchmarks/diesel/diesel/src/upsert/on_conflict_extension.rs create mode 100644 collector/benchmarks/diesel/diesel/src/util.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/Cargo.toml create mode 120000 collector/benchmarks/diesel/diesel_derives/LICENSE-APACHE create mode 120000 collector/benchmarks/diesel/diesel_derives/LICENSE-MIT create mode 100644 collector/benchmarks/diesel/diesel_derives/src/as_changeset.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/src/as_expression.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/src/associations.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/src/diagnostic_shim.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/src/diesel_numeric_ops.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/src/field.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/src/from_sql_row.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/src/identifiable.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/src/insertable.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/src/lib.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/src/meta.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/src/model.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/src/query_id.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/src/queryable.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/src/queryable_by_name.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/src/resolved_at_shim.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/src/sql_function.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/src/sql_type.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/src/util.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/src/valid_grouping.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/tests/as_changeset.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/tests/associations.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/tests/helpers.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/tests/identifiable.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/tests/insertable.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/tests/queryable.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/tests/queryable_by_name.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/tests/schema.rs create mode 100644 collector/benchmarks/diesel/diesel_derives/tests/tests.rs create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170207193805_initial_schema/down.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170207193805_initial_schema/up.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170209180355_add_one_off_tables_from_integration_tests/down.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170209180355_add_one_off_tables_from_integration_tests/up.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170211150830_index_columns_used_in_benchmarks/down.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170211150830_index_columns_used_in_benchmarks/up.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170215170122_create_trees/down.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170215170122_create_trees/up.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170219200159_add_foreign_key/down.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170219200159_add_foreign_key/up.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170407152306_add_nullable_table/down.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170407152306_add_nullable_table/up.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170603131224_add_likes/down.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170603131224_add_likes/up.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170804172328_add_foreign_keys/down.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170804172328_add_foreign_keys/up.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170811104602_add_blob_types/down.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170811104602_add_blob_types/up.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170816164349_add_keywords_table/down.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/20170816164349_add_keywords_table/up.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/2020-01-25-033332_add unsigned table/down.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/2020-01-25-033332_add unsigned table/up.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/2020-02-18-111430_create_pokes/down.sql create mode 100644 collector/benchmarks/diesel/migrations/mysql/2020-02-18-111430_create_pokes/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/00000000000000_diesel_initial_setup/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/00000000000000_diesel_initial_setup/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20151219180527_create_users_and_posts_and_comments/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20151219180527_create_users_and_posts_and_comments/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20160107090901_add_tags_to_posts/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20160107090901_add_tags_to_posts/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20160116104628_create_special_posts_and_special_comments/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20160116104628_create_special_posts_and_special_comments/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20160825135747_create_followings/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20160825135747_create_followings/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20161206123630_create_table_custom_schema/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20161206123630_create_table_custom_schema/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/2017-09-26-151545_fix_posts_tags/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/2017-09-26-151545_fix_posts_tags/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/2017-09-27-135928_remove_citext_table/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/2017-09-27-135928_remove_citext_table/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170209180355_add_one_off_tables_from_integration_tests/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170209180355_add_one_off_tables_from_integration_tests/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170211150830_index_columns_used_in_benchmarks/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170211150830_index_columns_used_in_benchmarks/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170215170122_create_trees/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170215170122_create_trees/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170219200159_add_foreign_key/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170219200159_add_foreign_key/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170407152306_add_nullable_table/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170407152306_add_nullable_table/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170603131224_add_likes/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170603131224_add_likes/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170717152137_add_ranges/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170717152137_add_ranges/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170721184144_create_citext_table/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170721184144_create_citext_table/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170804172328_add_foreign_keys/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170804172328_add_foreign_keys/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170816164352_add_keywords_table/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/20170816164352_add_keywords_table/up.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/2020-02-18-111430_create_pokes/down.sql create mode 100644 collector/benchmarks/diesel/migrations/postgresql/2020-02-18-111430_create_pokes/up.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20151219180527_create_users_and_posts_and_comments/down.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20151219180527_create_users_and_posts_and_comments/up.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20160116104628_create_special_posts_and_special_comments/down.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20160116104628_create_special_posts_and_special_comments/up.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20160416143735_infer_all_the_types/down.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20160416143735_infer_all_the_types/up.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20160825135747_create_followings/down.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20160825135747_create_followings/up.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170209180355_add_one_off_tables_from_integration_tests/down.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170209180355_add_one_off_tables_from_integration_tests/up.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170211150830_index_columns_used_in_benchmarks/down.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170211150830_index_columns_used_in_benchmarks/up.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170215170122_create_trees/down.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170215170122_create_trees/up.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170219200159_add_foreign_key/down.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170219200159_add_foreign_key/up.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170407152306_add_nullable_table/down.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170407152306_add_nullable_table/up.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170513174919_infer_all_the_datetime_types/down.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170513174919_infer_all_the_datetime_types/up.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170603131224_add_likes/down.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170603131224_add_likes/up.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170804172328_add_foreign_keys/down.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170804172328_add_foreign_keys/up.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170816164356_add_keywords_table/down.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/20170816164356_add_keywords_table/up.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/2020-02-18-111430_create_pokes/down.sql create mode 100644 collector/benchmarks/diesel/migrations/sqlite/2020-02-18-111430_create_pokes/up.sql create mode 100644 collector/benchmarks/diesel/perf-config.json diff --git a/collector/benchmarks/README.md b/collector/benchmarks/README.md index 2a4969ef4..c4091d840 100644 --- a/collector/benchmarks/README.md +++ b/collector/benchmarks/README.md @@ -41,6 +41,10 @@ These are real programs that are important in some way, and worth tracking. These are real programs that are known to stress the compiler in interesting ways. +- **diesel**: A type save SQL query builder. Utilizes the type system to + ensure a lot of invariants. Stresses anything related to resolving + trait bounds, by having a lot of trait impls for a large number of different + types. - **encoding**: Character encoding support. Contains some large tables. - **html5ever**: An HTML parser. Stresses macro parsing code significantly. - **inflate**: An old implementation of the DEFLATE algorithm. Stresses the diff --git a/collector/benchmarks/diesel/.editorconfig b/collector/benchmarks/diesel/.editorconfig new file mode 100644 index 000000000..d5188ec09 --- /dev/null +++ b/collector/benchmarks/diesel/.editorconfig @@ -0,0 +1,29 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 4 + +[*.rs] +indent_style = space +indent_size = 4 + +[*.toml] +indent_style = space +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_style = space +indent_size = 2 diff --git a/collector/benchmarks/diesel/.env.sample b/collector/benchmarks/diesel/.env.sample new file mode 100644 index 000000000..c3a6e365c --- /dev/null +++ b/collector/benchmarks/diesel/.env.sample @@ -0,0 +1,15 @@ +# The database to use when testing against Postgres. +PG_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/diesel_test +# The database to use when running the Postgres examples during testing. +PG_EXAMPLE_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/diesel_example + +# The database to use when testing against MySQL. +MYSQL_DATABASE_URL=mysql://root@127.0.0.1:3306/diesel_test +# The database to use when running the MySQL examples during testing. +MYSQL_EXAMPLE_DATABASE_URL=mysql://root@127.0.0.1:3306/diesel_example +# A database different from the others above used for certain unit tests. +# TODO: this is magical, explain what it's there for. +MYSQL_UNIT_TEST_DATABASE_URL=mysql://root@127.0.0.1:3306/diesel_unit_test + +# The database to use when testing against SQLite. +SQLITE_DATABASE_URL=/tmp/diesel_test.sqlite diff --git a/collector/benchmarks/diesel/.github/FUNDING.yml b/collector/benchmarks/diesel/.github/FUNDING.yml new file mode 100644 index 000000000..3df2b91d4 --- /dev/null +++ b/collector/benchmarks/diesel/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [sgrif, weiznich] diff --git a/collector/benchmarks/diesel/.github/ISSUE_TEMPLATE.md b/collector/benchmarks/diesel/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..2d89b9ac1 --- /dev/null +++ b/collector/benchmarks/diesel/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,51 @@ + + +## Setup + +### Versions + +- **Rust:** +- **Diesel:** +- **Database:** +- **Operating System** + +### Feature Flags + +- **diesel:** + +## Problem Description + + +### What are you trying to accomplish? + + +### What is the expected output? + + +### What is the actual output? + + +### Are you seeing any additional errors? + + +### Steps to reproduce + + + +## Checklist + +- [ ] I have already looked over the [issue tracker](https://github.com/diesel-rs/diesel/issues) for similar issues. +- [ ] This issue can be reproduced on Rust's stable channel. (Your issue will be + closed if this is not the case) + + diff --git a/collector/benchmarks/diesel/.github/workflows/audit.yml b/collector/benchmarks/diesel/.github/workflows/audit.yml new file mode 100644 index 000000000..1f37f8bb1 --- /dev/null +++ b/collector/benchmarks/diesel/.github/workflows/audit.yml @@ -0,0 +1,16 @@ +name: Security audit +on: + push: + paths: + - '**/Cargo.toml' + - '**/Cargo.lock' + schedule: + - cron: '0 0 */7 * *' +jobs: + security_audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/audit-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/collector/benchmarks/diesel/.github/workflows/benches.yml b/collector/benchmarks/diesel/.github/workflows/benches.yml new file mode 100644 index 000000000..970ef57f4 --- /dev/null +++ b/collector/benchmarks/diesel/.github/workflows/benches.yml @@ -0,0 +1,53 @@ +on: + pull_request: + types: + - labeled + +name: Benchmarks + +jobs: + benchmarks: + if: github.event.label.name == 'run-benchmarks' + runs-on: ubuntu-latest + strategy: + matrix: + backend: ["postgres", "sqlite", "mysql"] + steps: + - name: Checkout sources + if: steps.bench.outputs.triggered == 'true' + uses: actions/checkout@v2 + + - name: Install postgres (Linux) + if: matrix.backend == 'postgres' + run: | + sudo apt-get update + sudo apt-get install -y libpq-dev postgresql + echo "host all all 127.0.0.1/32 md5" > sudo tee -a /etc/postgresql/10/main/pg_hba.conf + sudo service postgresql restart && sleep 3 + sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'postgres';" + sudo service postgresql restart && sleep 3 + echo '::set-env name=PG_DATABASE_URL::postgres://postgres:postgres@localhost/' + + - name: Install sqlite (Linux) + if: matrix.backend == 'sqlite' + run: | + sudo apt-get update + sudo apt-get install -y libsqlite3-dev + echo '::set-env name=SQLITE_DATABASE_URL::/tmp/test.db' + + + - name: Install mysql (Linux) + if: matrix.backend == 'mysql' + run: | + sudo apt-get update + sudo apt-get -y install mysql-server libmysqlclient-dev + sudo /etc/init.d/mysql start + mysql -e "create database diesel_test; create database diesel_unit_test; grant all on \`diesel_%\`.* to 'root'@'localhost';" -uroot -proot + echo '::set-env name=MYSQL_DATABASE_URL::mysql://root:root@localhost/diesel_test' + + - name: Run benches + uses: jasonwilliams/criterion-compare-action@move_to_actions + with: + cwd: "diesel_bench" + token: ${{ secrets.GITHUB_TOKEN }} + diff --git a/collector/benchmarks/diesel/.github/workflows/ci.yml b/collector/benchmarks/diesel/.github/workflows/ci.yml new file mode 100644 index 000000000..891001d1a --- /dev/null +++ b/collector/benchmarks/diesel/.github/workflows/ci.yml @@ -0,0 +1,426 @@ +on: + pull_request: + types: [opened, synchronize, reopened] + push: + branches: + - master + - '0.[0-9]+.x' + - '1.[0-9]+.x' + + +name: CI Tests + +jobs: + check_and_test: + name: Check + strategy: + fail-fast: false + matrix: + rust: ["stable", "beta", "nightly"] + backend: ["postgres", "sqlite", "mysql"] + os: [ubuntu-20.04, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Cache cargo registry + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ runner.os }}-${{ matrix.backend }}-cargo-${{ hashFiles('**/Cargo.toml') }} + + - name: Set environment variables + shell: bash + if: matrix.backend == 'mysql' + run: | + echo "RUST_TEST_THREADS=1" >> $GITHUB_ENV + + - name: Set environment variables + shell: bash + if: matrix.rust == 'nightly' + run: | + echo "RUSTFLAGS=--cap-lints=warn" >> $GITHUB_ENV + + - name: Install postgres (Linux) + if: runner.os == 'Linux' && matrix.backend == 'postgres' + run: | + sudo apt-get update + sudo apt-get install -y libpq-dev postgresql + echo "host all all 127.0.0.1/32 md5" > sudo tee -a /etc/postgresql/10/main/pg_hba.conf + sudo service postgresql restart && sleep 3 + sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'postgres';" + sudo service postgresql restart && sleep 3 + echo "PG_DATABASE_URL=postgres://postgres:postgres@localhost/" >> $GITHUB_ENV + echo "PG_EXAMPLE_DATABASE_URL=postgres://postgres:postgres@localhost/diesel_example" >> $GITHUB_ENV + + - name: Install sqlite (Linux) + if: runner.os == 'Linux' && matrix.backend == 'sqlite' + run: | + curl -fsS --retry 3 -o sqlite-autoconf-3310100.tar.gz https://sqlite.org/2020/sqlite-autoconf-3310100.tar.gz + tar zxf sqlite-autoconf-3310100.tar.gz + cd sqlite-autoconf-3310100 + CFLAGS="$CFLAGS -O2 -fno-strict-aliasing \ + -DSQLITE_DEFAULT_FOREIGN_KEYS=1 \ + -DSQLITE_SECURE_DELETE \ + -DSQLITE_ENABLE_COLUMN_METADATA \ + -DSQLITE_ENABLE_FTS3_PARENTHESIS \ + -DSQLITE_ENABLE_RTREE=1 \ + -DSQLITE_SOUNDEX=1 \ + -DSQLITE_ENABLE_UNLOCK_NOTIFY \ + -DSQLITE_OMIT_LOOKASIDE=1 \ + -DSQLITE_ENABLE_DBSTAT_VTAB \ + -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 \ + -DSQLITE_ENABLE_LOAD_EXTENSION \ + -DSQLITE_ENABLE_JSON1 \ + -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \ + -DSQLITE_THREADSAFE=1 \ + -DSQLITE_ENABLE_FTS3_TOKENIZER=1 \ + -DSQLITE_MAX_SCHEMA_RETRY=25 \ + -DSQLITE_ENABLE_PREUPDATE_HOOK \ + -DSQLITE_ENABLE_SESSION \ + -DSQLITE_ENABLE_STMTVTAB \ + -DSQLITE_MAX_VARIABLE_NUMBER=250000" \ + ./configure --prefix=/usr \ + --enable-threadsafe \ + --enable-dynamic-extensions \ + --libdir=/usr/lib/x86_64-linux-gnu \ + --libexecdir=/usr/lib/x86_64-linux-gnu/sqlite3 + sudo make + sudo make install + echo "SQLITE_DATABASE_URL=/tmp/test.db" >> $GITHUB_ENV + + - name: Install mysql (Linux) + if: runner.os == 'Linux' && matrix.backend == 'mysql' + run: | + sudo apt-get update + sudo apt-get -y install mysql-server libmysqlclient-dev + sudo /etc/init.d/mysql start + mysql -e "create database diesel_test; create database diesel_unit_test; grant all on \`diesel_%\`.* to 'root'@'localhost';" -uroot -proot + echo "MYSQL_DATABASE_URL=mysql://root:root@localhost/diesel_test" >> $GITHUB_ENV + echo "MYSQL_EXAMPLE_DATABASE_URL=mysql://root:root@localhost/diesel_example" >> $GITHUB_ENV + echo "MYSQL_UNIT_TEST_DATABASE_URL=mysql://root:root@localhost/diesel_unit_test" >> $GITHUB_ENV + + - name: Install postgres (MacOS) + if: runner.os == 'macOS' && matrix.backend == 'postgres' + run: | + /usr/local/opt/postgres/bin/pg_ctl -D /usr/local/var/postgres start + sleep 3 + /usr/local/opt/postgres/bin/createuser -s postgres + echo "PG_DATABASE_URL=postgres://postgres@localhost/" >> $GITHUB_ENV + echo "PG_EXAMPLE_DATABASE_URL=postgres://postgres@localhost/diesel_example" >> $GITHUB_ENV + + - name: Install sqlite (MacOS) + if: runner.os == 'macOS' && matrix.backend == 'sqlite' + run: | + brew uninstall openssl@1.0.2t + brew uninstall python@2.7.17 + brew untap local/openssl + brew untap local/python2 + brew cask install xquartz + brew update + brew install sqlite + echo "SQLITE_DATABASE_URL=/tmp/test.db" >> $GITHUB_ENV + + - name: Install mysql (MacOS) + if: runner.os == 'macOS' && matrix.backend == 'mysql' + run: | + brew uninstall openssl@1.0.2t + brew uninstall python@2.7.17 + brew untap local/openssl + brew untap local/python2 + brew cask install xquartz + brew update + brew install mysql + brew services start mysql + brew services stop mysql;sleep 3;brew services start mysql + sleep 3 + macos_mysql_version="$(ls /usr/local/Cellar/mysql)" + /usr/local/Cellar/mysql/${macos_mysql_version}/bin/mysql -e "create database diesel_test; create database diesel_unit_test; grant all on \`diesel_%\`.* to 'root'@'localhost';" -uroot + echo "MYSQL_DATABASE_URL=mysql://root@localhost/diesel_test" >> $GITHUB_ENV + echo "MYSQL_EXAMPLE_DATABASE_URL=mysql://root@localhost/diesel_example" >> $GITHUB_ENV + echo "MYSQL_UNIT_TEST_DATABASE_URL=mysql://root@localhost/diesel_unit_test" >> $GITHUB_ENV + echo "MYSQLCLIENT_LIB_DIR=/usr/local/Cellar/mysql/${macos_mysql_version}/lib" >> $GITHUB_ENV + + - name: Install sqlite (Windows) + if: runner.os == 'Windows' && matrix.backend == 'sqlite' + shell: cmd + run: | + choco install sqlite + cd /D C:\ProgramData\chocolatey\lib\SQLite\tools + call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + lib /machine:x64 /def:sqlite3.def /out:sqlite3.lib + + - name: Set variables for sqlite (Windows) + if: runner.os == 'Windows' && matrix.backend == 'sqlite' + shell: bash + run: | + echo "C:\ProgramData\chocolatey\lib\SQLite\tools" >> $GITHUB_PATH + echo "SQLITE3_LIB_DIR=C:\ProgramData\chocolatey\lib\SQLite\tools" >> $GITHUB_ENV + echo "SQLITE_DATABASE_URL=C:\test.db" >> $GITHUB_ENV + + - name: Install postgres (Windows) + if: runner.os == 'Windows' && matrix.backend == 'postgres' + shell: bash + run: | + choco install postgresql12 --force --params '/Password:root' + echo "C:\Program Files\PostgreSQL\12\bin" >> $GITHUB_PATH + echo "C:\Program Files\PostgreSQL\12\lib" >> $GITHUB_PATH + echo "PQ_LIB_DIR=C:\Program Files\PostgreSQL\12\lib" >> $GITHUB_ENV + echo "PG_DATABASE_URL=postgres://postgres:root@localhost/" >> $GITHUB_ENV + echo "PG_EXAMPLE_DATABASE_URL=postgres://postgres:root@localhost/diesel_example" >> $GITHUB_ENV + + - name: Install mysql (Windows) + if: runner.os == 'Windows' && matrix.backend == 'mysql' + shell: cmd + run: | + choco install mysql + "C:\tools\mysql\current\bin\mysql" -e "create database diesel_test; create database diesel_unit_test; grant all on `diesel_%`.* to 'root'@'localhost';" -uroot + + - name: Set variables for mysql (Windows) + if: runner.os == 'Windows' && matrix.backend == 'mysql' + shell: bash + run: | + echo "MYSQL_DATABASE_URL=mysql://root@localhost/diesel_test" >> $GITHUB_ENV + echo "MYSQL_EXAMPLE_DATABASE_URL=mysql://root@localhost/diesel_example" >> $GITHUB_ENV + echo "MYSQL_UNIT_TEST_DATABASE_URL=mysql://root@localhost/diesel_unit_test" >> $GITHUB_ENV + echo "MYSQLCLIENT_LIB_DIR=C:\tools\mysql\current\lib" >> $GITHUB_ENV + + - name: Install rust toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + override: true + - name: Rust version check + uses: actions-rs/cargo@v1 + with: + command: version + - name: Test diesel (nightly) + if: matrix.rust == 'nightly' + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel/Cargo.toml --no-default-features --features "${{ matrix.backend }} unstable extras" + + - name: Test diesel + if: matrix.rust != 'nightly' + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel/Cargo.toml --no-default-features --features "${{ matrix.backend }} extras" + + - name: Test diesel (with-deprecated) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel/Cargo.toml --no-default-features --features "${{ matrix.backend }} extras with-deprecated" + + - name: Test diesel-derives (nightly) + if: matrix.rust == 'nightly' + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_derives/Cargo.toml --no-default-features --features "diesel/${{ matrix.backend }} diesel/unstable" + + - name: Test diesel-derives + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_derives/Cargo.toml --no-default-features --features "diesel/${{ matrix.backend }}" + + - name: Test diesel-cli + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_cli/Cargo.toml --no-default-features --features "${{ matrix.backend }}" + + - name: Test diesel examples + shell: bash + run: | + (cd examples/${{ matrix.backend }} && BACKEND=${{ matrix.backend }} ./test_all) + + - name: Test migrations-internals + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_migrations/migrations_internals/Cargo.toml + + - name: Test migrations-macros + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_migrations/migrations_macros/Cargo.toml --features "diesel/${{ matrix.backend }} ${{ matrix.backend }}" + + - name: Test diesel_migrations + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_migrations/Cargo.toml --features "${{ matrix.backend }} diesel/${{ matrix.backend }}" + + - name: Run diesel_tests (nightly) + if: matrix.run == 'nightly' + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_tests/Cargo.toml --no-default-features --features "${{ matrix.backend }} unstable" + + - name: Run diesel_tests + if: matrix.run != 'nightly' + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_tests/Cargo.toml --no-default-features --features "${{ matrix.backend }}" + + - name: Run diesel_dynamic_schema tests + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_dynamic_schema/Cargo.toml --no-default-features --features "${{ matrix.backend }}" + + - name: Run diesel_benches + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_bench/Cargo.toml --no-default-features --features "${{ matrix.backend }}" --bench benchmarks + + - name: Run rustdoc (nightly) + if: matrix.run == 'nightly' + uses: actions-rs/cargo@v1 + with: + command: doc + args: --manifest-path diesel/Cargo.toml --no-deps --no-default-features --features "${{ matrix.backend }} unstable" + + - name: Run rustdoc + if: matrix.run != 'nightly' + uses: actions-rs/cargo@v1 + with: + command: doc + args: --manifest-path diesel/Cargo.toml --no-deps --no-default-features --features "${{ matrix.backend }}" + + compile_tests: + name: Compiletests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly-2020-05-01 + profile: minimal + override: true + - name: Cache cargo registry + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: compile_test-cargo-${{ hashFiles('**/Cargo.toml') }} + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get -y install libsqlite3-dev libpq-dev libmysqlclient-dev + + - name: Run compile tests + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_compile_tests/Cargo.toml + + rustfmt_and_clippy: + name: Check rustfmt style && run clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.40.0 + profile: minimal + components: clippy, rustfmt + override: true + - name: Cache cargo registry + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: clippy-cargo-${{ hashFiles('**/Cargo.toml') }} + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get -y install libsqlite3-dev libpq-dev libmysqlclient-dev + + - name: Run clippy + uses: actions-rs/cargo@v1 + with: + command: clippy + + - name: Check formating + uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + sqlite_bundled: + name: Check sqlite bundled + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + - name: Cache cargo registry + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: sqlite_bundled-cargo-${{ hashFiles('**/Cargo.toml') }} + + - name: Setup environment + run: | + echo "SQLITE_DATABASE_URL=/tmp/test.db" >> $GITHUB_ENV + + - name: Test diesel-cli + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_cli/Cargo.toml --no-default-features --features "sqlite-bundled" + + minimal_rust_version: + name: Check Minimal supported rust version (1.40.0) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.40.0 + profile: minimal + override: true + - name: Cache cargo registry + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: minimal_rust_version-cargo-${{ hashFiles('**/Cargo.toml') }} + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get -y install libsqlite3-dev libpq-dev libmysqlclient-dev + - name: Use minimal dependencies + run: | + RUSTC_BOOTSTRAP=1 cargo update -Z minimal-versions + + - name: Check building with rust 1.40.0 + uses: actions-rs/cargo@v1 + with: + command: check + # This example pulls generic-array v0.14.1 that cannot compile with rustc 1.40.0. + # We could exclude it as it won't affect our MSRV. + args: --all --exclude=advanced-blog-cli diff --git a/collector/benchmarks/diesel/.github/workflows/doc.yml b/collector/benchmarks/diesel/.github/workflows/doc.yml new file mode 100644 index 000000000..45386eaab --- /dev/null +++ b/collector/benchmarks/diesel/.github/workflows/doc.yml @@ -0,0 +1,54 @@ +on: + push: + branches: + - master + - '0.[0-9]+.x' + - '1.[0-9]+.x' + +name: Publish Docs +jobs: + publish_docs: + if: github.repository == 'diesel-rs/diesel' + name: Publish Docs + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + - name: Cache cargo registry + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: cargo-doc-cargo-${{ hashFiles('**/Cargo.toml') }} + - name: Get the branch name + id: current_branch + shell: bash + run: | + echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})" + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get -y install libmysqlclient-dev libsqlite3-dev libpq-dev + - name: Build documentation + uses: actions-rs/cargo@v1 + with: + command: doc + args: --manifest-path diesel/Cargo.toml --features "postgres sqlite mysql extras" --workspace + + - name: Publish documentation + if: success() + uses: JamesIves/github-pages-deploy-action@releases/v3 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: gh-pages # The branch the action should deploy to. + FOLDER: target/doc # The folder the action should deploy. + # Store documentation for each branch in a different folder + # This allows us to differentiate between docs for master + # and docs for already released versions + TARGET_FOLDER: ${{ steps.current_branch.outputs.branch }} diff --git a/collector/benchmarks/diesel/.gitignore b/collector/benchmarks/diesel/.gitignore new file mode 100644 index 000000000..d4da73439 --- /dev/null +++ b/collector/benchmarks/diesel/.gitignore @@ -0,0 +1,4 @@ +target +Cargo.lock +!diesel_cli/Cargo.lock +.env \ No newline at end of file diff --git a/collector/benchmarks/diesel/CHANGELOG.md b/collector/benchmarks/diesel/CHANGELOG.md new file mode 100644 index 000000000..fd0fc965d --- /dev/null +++ b/collector/benchmarks/diesel/CHANGELOG.md @@ -0,0 +1,1867 @@ +# Change Log + +All user visible changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/), as described +for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/text/1105-api-evolution.md) + +## Unreleased + +### Added + +* `Connection` and `SimpleConnection` traits are implemented for a broader range + of `r2d2::PooledConnection` types when the `r2d2` feature is enabled. + +* Added `DatabaseErrorKind::ReadOnlyTransaction` to allow applications to + handle errors caused by writing when only allowed to read. + +* All expression methods can now be called on expressions of nullable types. + +* Added `BoxedSqlQuery`. This allows users to do a variable amount of `.sql` or + `.bind` calls without changing the underlying type. + +* Added `.sql` to `SqlQuery` and `UncheckedBind` to allow appending SQL code to + an existing query. + +* The `MacAddr` SQL type can now be used without enabling the `network-address` + feature. + +* Added support for SQLite's `UPSERT`. + You can use this feature above SQLite version 3.24.0. + +* Added ability to create custom aggregate functions in SQLite. + +* Multiple aggregate expressions can now appear together in the same select + clause. See [the upgrade notes](#2-0-0-upgrade-non-aggregate) for details. + +* `ValidGrouping` has been added to represent whether an expression is valid for + a given group by clause, and whether or not it's aggregate. It replaces the + functionality of `NonAggregate`. See [the upgrade + notes](#2-0-0-upgrade-non-aggregate) for details. + +* It is now possible to inspect the type of values returned from the database + in such a way to support constructing a dynamic value depending on this type. + +* Added a `without-deprecated` feature that unconditionally disables deprecated items. + Use this feature flag to verify that none of your dependencies is setting + the `with-deprecated` flag internally. + +* Added support for PostgreSQL's `SIMILAR TO` and `NOT SIMILAR TO`. + +* Added `#[diesel(serialize_as)]` analogous to `#[diesel(deserialize_as)]`. This allows + customization of the serialization behaviour of `Insertable` structs. + +* Added support for `GROUP BY` clauses + +### Removed + +* All previously deprecated items have been removed. +* Support for `uuid` version < 0.7.0 has been removed. +* Support for `bigdecimal` < 0.0.13 has been removed. +* Support for `pq-sys` < 0.4.0 has been removed. +* Support for `mysqlclient-sys` < 0.2.0 has been removed. +* Support for `time` types has been removed. +* Support for `chrono` < 0.4.19 has been removed. +* The `NonNull` for sql types has been removed in favour of the new `SqlType` trait. + +* `no_arg_sql_function!` has been deprecated without replacement. + [`sql_function!`][sql-function-2-0-0] can now be used for functions with zero + arguments. See [the migration guide][2-0-migration] for more details. + +### Changed + +* The way [the `Backend` trait][backend-2-0-0] handles its `RawValue` type has + been changed to allow non-references. Users of this type (e.g. code written + `&DB::RawValue` or `&::RawValue>`) should use + [`backend::RawValue`][raw-value-2-0-0] instead. Implementors of `Backend` + should check the relevant section of [the migration guide][2-0-migration]. + +[backend-2-0-0]: http://docs.diesel.rs/diesel/backend/trait.Backend.html +[raw-value-2-0-0]: http://docs.diesel.rs/diesel/backend/type.RawValue.html + +* The type metadata for MySQL has been changed to include sign information. If + you are implementing `HasSqlType` for `Mysql` manually, you may need to adjust + your implementation to fully use the new unsigned variants in `MysqlType` + +* The minimal officially supported rustc version is now 1.40.0 + +* The `RawValue` types for the `Mysql` and `Postgresql` backend where changed + from `[u8]` to distinct opaque types. If you used the concrete `RawValue` type + somewhere you need to change it to `mysql::MysqlValue` or `pg::PgValue`. + +* The uuidv07 feature was renamed to uuid, due to the removal of support for older uuid versions + +* Boxed queries (constructed from `.into_boxed()`) are now `Send`. + +* The handling of mixed aggregate values is more robust. Invalid queries such as + `.select(max(id) + other_column)` are now correctly rejected, and valid + queries such as `.select((count_star(), max(other_column)))` are now correctly + accepted. For more details, see [the upgrade notes](#2-0-0-upgrade-non-aggregate). + +* `NonAggregate` is now a trait alias for `ValidGrouping<()>` for expressions + that are not aggregate. On stable this is a normal trait with a blanket impl, + but it should never be implemented directly. With the `unstable` feature, it + will use trait aliases which prevent manual implementations. + + Due to language limitations, we cannot make the new trait alias by itself + represent everything it used to, so in some rare cases code changes may be + required. See [the upgrade notes](#2-0-0-upgrade-non-aggregate) for details. + +* Various `__NonExhaustive` variants in different (error-) enums are replaced with + `#[non_exhaustive]`. If you matched on one of those variants explicitly you need to + introduce a wild card match instead. + +* `FromSql::from_sql` is changed to construct value from non nullable database values. + To construct a rust value for nullable values use the new `FromSql::from_nullable_sql` + method instead. + +* Custom sql types are now required to implement the new `SqlType` trait. Diesel will + automatically create implementations of that trait for all types having a `#[derive(SqlType)]` + +* The workflow for manually implementing support custom types has changed. Implementing + `FromSqlRow` is not required anymore, as this is now implied by implementing + `FromSql`. The requirement of implementing `Queryable` remains + unchanged. For types using `#[derive(FromSqlRow)]` no changes are required as the + derive automatically generates the correct code + +* The structure of our deserialization trait has changed. Loading values from the database + requires now that the result type implements `FromSqlRow`. Diesel provides wild + card implementations for types implementing `Queryable` or `QueryableByName` + so non generic code does not require any change. For generic code you likely need to + replace a trait bound on `Queryable` with a trait bound on `FromSqlRow` + and a bound to `QueryableByName` with `FromSqlRow`. + +* Diesel's dsl now accept not nullable expressions in positions where nullable expressions + are expected, without needing to call `.nullable()` explicitly + +* CLI flags of `only-tables` and `except-tables` are now interpreted as regular expressions. + Similary, `only_tabels` and `except_tables` in `diesel.toml` are treated as regular expressions. + +* Now you can sort column fields by name with the `column-sorting` option. +It can be set to either `ordinal_position` (default) or `name`. +This ensures stable sorting even if columns are removed and re-added. + +### Fixed + +* Many types were incorrectly considered non-aggregate when they should not + have been. All types in Diesel are now correctly only considered + non-aggregate if their parts are. + +* Offset clauses without limit clauses resulted into invalid sql using the mysql or + sqlite backend. Both do not support such clauses without a preceding limit clause. + For those backend Diesel does now generate a fake limit clause in case no explicit + limit clause was given. As consequence of this change generic query code may + require additional trait bounds as requested from the compiler. Third party + backends are required to explicitly provide `QueryFragment` impls for + `LimitOffsetClause` now. + +* Nullability requirements are now properly enforced for nested joins. + Previously, only the rules for the outer-most join were considered. For + example, `users.left_join(posts).left_join(comments)` would allow selecting + any columns from `posts`. That will now fail to compile, and any selections + from `posts` will need to be made explicitly nullable. + +* Diesel CLI will now look for `diesel.toml` to determine the project root + before looking for `Cargo.toml`. + +* Any relative paths in `diesel.toml` will now be treated as relative to the + project root (the directory containing either `diesel.toml` or `Cargo.toml`). + They are no longer dependent on the current working directory (for all + directories in the same project) + +* The SQLite backend is now configured to interpret URIs. + See [the SQLite URI documentation] for additional details. + +[the SQLite URI documentation]: https://www.sqlite.org/uri.html + +* We've refactored our type translation layer for Mysql to handle more types now. + +* We've refactored our type level representation of nullable values. This allowed us to + fix multiple long standing bugs regarding the correct handling of nullable values in some + corner cases (#104, #2274) + +* Parenthesis are now inserted around all infix operations provided by diesel's `ExpressionMethods` traits + +### Deprecated + +* `diesel_(prefix|postfix|infix)_operator!` have been deprecated. These macros + are now available without the `diesel_` prefix. With Rust 2018 they can be + invoked as `diesel::infix_operator!` instead. + +* `diesel::pg::upsert` has been deprecated to support upsert queries on more than one backend. + Please use `diesel::upsert` instead. + +### Upgrade Notes + +#### Replacement of `NonAggregate` with `ValidGrouping` + + +FIXME: This should probably be on the website, but I wanted to document it in +the PR adding the changes. + +Key points: + +- Rules for aggregation are now correctly enforced. They match the semantics of + PG or MySQL with `ONLY_FULL_GROUP_BY` enabled. + - As before, `sql` is the escape hatch if needed. + - MySQL users can use `ANY_VALUE`, PG users can use `DISTINCT ON`. Also + consider using max/min/etc to get deterministic values. +- Any `impl NonAggregate` must be replaced with `impl ValidGrouping` +- For most code, `T: NonAggregate` should continue to work. Unless you're + getting a compiler error, you most likely don't need to change it. +- The full equivalent of what `T: NonAggregate` used to mean is: + + where + T: ValidGrouping<()>, + T::IsAggregate: MixedGrouping, + is_aggreagte::No: MixedGrouping, + +- With `feature = "unstable"`, `T: NonAggregate` implies the first two bounds, + but not the third. On stable only the first bound is implied. This is a + language limitation. +- `T: NonAggregate` can still be passed everywhere it could before, but `T: + NonAggregate` no longer implies `(OtherType, T): NonAggregate`. + - With `feature = "unstable"`, `(T, OtherType): NonAggregate` is still implied. + +- In diesel v1.4 sql functions without arguments used the `no_arg_sql_function!` macro, + which has since been deprecated. The new `sql_function!` macro supports functions without + arguments. + +[2-0-migration]: FIXME write a migration guide + +## [1.4.5] - 2020-06-09 + +### Fixed + +* Update several dependencies +* Fixed an issue where transactions that would fail to commit would leave the connection + in a broken non-committed non-rolled-back state. +* Fix a bug that result in leaking sockets/file descriptors on failed connection attempts + for postgresql +* Fix an incompatibility with newer `libmysqlclient` versions +* Remove some potential harmful usages of `mem::uninitialized` + +## [1.4.4] - 2020-03-22 + +### Fixed + +* Update several dependencies +* Fixed a bug with printing embeded migrations + +## [1.4.3] - 2019-10-11 + +### Fixed + +* Updated several dependencies +* Fixed an issue where the postgresql backend exploits implementation defined behaviour +* Fixed issue where rustdoc failed to build the documentation +* `diesel_derives` and `diesel_migrations` are updated to syn 1.0 + + +## [1.4.2] - 2019-03-19 + +### Fixed + +* Parenthesis are now inserted around all mathematical operations. This means + that `(2.into_sql() + 3) * 4` will correctly evaluate to 20 as expected. + Previously we would generate SQL that evaluated to 14. This could even result + in runtime errors if multiple types were involved (for example, `interval * + (integer + 1)`) + +## [1.4.1] - 2019-01-24 + +### Fixed + +* This release fixes a minor memory safety issue in SQLite. This bug would only + occur in an error handling branch that should never occur in practice. + +## [1.4.0] - 2019-01-20 + +### Fixed + +* `embed_migrations!` will no longer emit an unused import warning +* Diesel now supports uuid 0.7 by adding the new feature flag `uuidv07` + +### Added + +* Diesel CLI can be configured to error if a command would result in changes + to your schema file by passing `--locked-schema`. This is intended for use + in CI and production deploys, to ensure that the committed schema file is + up to date. + +* A helper trait has been added for implementing `ToSql` for PG composite types. + See [`WriteTuple`][write-tuple-1-4-0] for details. + +[write-tuple-1-4-0]: docs.diesel.rs/diesel/serialize/trait.WriteTuple.html + +* Added support for MySQL's `UNSIGNED TINYINT` + +* `DatabaseErrorKind::SerializationFailure` has been added, corresponding to + SQLSTATE code 40001 (A `SERIALIZABLE` isolation level transaction failed to + commit due to a read/write dependency on another transaction). This error is + currently only detected on PostgreSQL. + +* Diesel CLI can now generate completions for zsh and fish. See `diesel + completions --help` for details. + +* `#[belongs_to]` can now accept types that are generic over lifetimes (for + example, if one of the fields has the type `Cow<'a, str>`). To define an + association to such a type, write `#[belongs_to(parent = "User<'_>")]` + +* `Nullable` now supports `ilike` expression on in PostgreSQL. + +* `diesel_manage_updated_at('table_name')` is now available on SQLite. This + function can be called in your migrations to create a trigger which + automatically sets the `updated_at` column, unless that column was updated in + the query. + +### Changed + +* Diesel's derives now require that `extern crate diesel;` be at your crate root + (e.g. `src/lib.rs` or `src/main.rs`) + +* `Tinyint` has been renamed to `TinyInt` and an alias has been created from `Tinyint` to `TinyInt`. + +* The minimal officially supported rustc version is now 1.31.0 + +## [1.3.3] - 2018-09-12 + +### Fixed + +* Fixed an issue that occurred with MySQL 8.0 when calling `.execute` or + `.batch_execute` with a single query that returned a result set (such as our + `SELECT 1` health check in `r2d2`). + +## [1.3.2] - 2018-06-13 + +### Fixed + +* The behavior of unsigned types in MySQL has been corrected to properly set the + `is_unsigned` flag. + +* Fixed an issue with `sql_function!` when `#[sql_name]` was used on functions + with no return type. + +## [1.3.1] - 2018-05-23 + +### Fixed + +* Fixed an issue with Diesel CLI's use of temp files that caused errors on + Windows. + +## [1.3.0] - 2018-05-22 + +### Added + +* Diesel CLI now supports a configuration file. See + diesel.rs/guides/configuring-diesel-cli for details. + +* `sql_function!` now supports generic functions. See [the documentation for + `sql_function!`][sql-function-1-3-0] for more details. + +* `sql_function!` now supports aggregate functions like `sum` and `max`, by + annotating them with `#[aggregate]`. This skips the implementation of + `NonAggregate` for your function. See [the documentation for + `sql_function!`][sql-function-1-3-0] for more details. + +* `sql_function!` now supports renaming the function by annotating it with + `#[sql_name = "SOME_FUNCTION"]`. This can be used to support functions with + multiple signatures such as coalesce, by defining multiple rust functions + (with different names) that have the same `#[sql_name]`. + +* Added `sqlite-bundled` feature to `diesel_cli` to make installing on + some platforms easier. + +* Custom SQL functions can now be used with SQLite. See [the + docs][sql-function-sqlite-1-3-0] for details. + +[sql-function-sqlite-1-3-0]: http://docs.diesel.rs/diesel/macro.sql_function.html#use-with-sqlite + +* All functions and operators provided by Diesel can now be used with numeric + operators if the SQL type supports it. + +* `PgInterval` can now be used with `-`, `*`, and `/`. + +* `Vec` is now `Insertable`. It is no longer required to always place an `&` + in front of `.values`. + +* Added support for PG tuples. See [`sql_types::Record`][record-1-3-0] for details. + +[record-1-3-0]: http://docs.diesel.rs/diesel/pg/types/sql_types/struct.Record.html + +* Added support for a wider range of locking clauses, including `FOR SHARE`, + `SKIP LOCKED`, `NO WAIT`, and more. See [`QueryDsl`][locking-clause-1-3-0] for details. + +[locking-clause-1-3-0]: http://docs.diesel.rs/diesel/query_dsl/trait.QueryDsl.html#method.for_update + +### Changed + +* `sql_function!` has been redesigned. The syntax is now `sql_function!(fn + lower(x: Text) -> Text);`. The output of the new syntax is slightly different + than what was generated in the past. See [the documentation for + `sql_function!`][sql-function-1-3-0] for more details. + +[sql-function-1-3-0]: http://docs.diesel.rs/diesel/macro.sql_function.html + +* Diesel's minimum supported Rust version is 1.24.0. This was already true, but + it is now tested and enforced. Any future changes to our minimum supported + version will be listed in this change log. + +### Fixed + +* `diesel print-schema` and `infer_schema!` now properly handle unsigned types + in MySQL + +### Deprecated + +* `diesel_infer_schema` has been deprecated. `diesel print-schema` is now the + only way to generate database schema. Diesel CLI can be configured to + automatically regenerate your schema file when migrations are run. See + diesel.rs/guides/configuring-diesel-cli for details. + +* Uses of `sql_function!` in the form `sql_function!(foo, foo_t, (x: Integer))` + have been deprecated in favor of a new design (listed above). Note: Due to [a + bug in Rust](https://github.com/rust-lang/rust/issues/49912), you may not see + a deprecation warning from usage of the old form. As always, if you're + concerned about relying on deprecated code, we recommend attempting to build + your app with `default-features` turned off (specifically excluding the + `with-deprecated` feature). + +* The `--whitelist` and `--blacklist` options to `diesel print-schema` have been + deprecated and renamed `--only-tables` and `--exclude-tables`. + +## [1.2.2] - 2018-04-12 + +### Changed + +* Warnings are now allowed inside the crate. The way we had attempted to + deprecate old feature names caused builds to break. We are still not happy + with how this deprecation gets communicated, and will revisit it in the + future. + +## [1.2.1] - 2018-04-11 + +### Changed + +* Renamed `x32-column-tables`, `x64-column-tables`, and `x128-column-tables` to + `32-column-tables`, `64-column-tables`, and `128-column-tables`. The leading + `x` was due to a bug in crates.io discovered while publishing 1.2.0. The bug + has since been fixed. + +## [1.2.0] - 2018-04-06 + +### Added + +* Added `SqlLiteral::bind()`. + This is intended to be used for binding values to small SQL fragments. + Use `sql_query` if you are writing full queries. + +* Added support for `INSERT INTO table (...) SELECT ...` queries. Tables, select + select statements, and boxed select statements can now be used just like any + other `Insertable` value. + +* Any insert query written as `insert_into(table).values(values)` can now be + written as `values.insert_into(table)`. This is particularly useful when + inserting from a select statement, as select statements tend to span multiple + lines. + +* Diesel's derives can now produce improved error messages if you are using a + nightly compiler, and enable the `unstable` feature. For the best errors, you + should also set `RUSTFLAGS="--cfg procmacro2_semver_exempt"`. + +* Added support for specifying `ISOLATION LEVEL`, `DEFERRABLE`, and `READ ONLY` + on PG transactions. See [`PgConnection::build_transaction`] for details. + +[`PgConnection::build_transaction`]: http://docs.diesel.rs/diesel/pg/struct.PgConnection.html#method.build_transaction + +* Added support for `BEGIN IMMEDIATE` and `BEGIN EXCLUSIVE` on SQLite. + See [`SqliteConnection::immediate_transaction`] and + [`SqliteConnection::exclusive_transaction`] for details + +[`SqliteConnection::immediate_transaction`]: http://docs.diesel.rs/diesel/sqlite/struct.SqliteConnection.html#method.immediate_transaction +[`SqliteConnection::exclusive_transaction`]: http://docs.diesel.rs/diesel/sqlite/struct.SqliteConnection.html#method.exclusive_transaction + +* Tables with more than 56 columns are now supported by enabling the + `128-column-tables` feature. + +* Delete statements can now be boxed. This is useful for conditionally modifying + the where clause of a delete statement. See [`DeleteStatement::into_boxed`] + for details. + +[`DeleteStatement::into_boxed`]: http://docs.diesel.rs/diesel/query_builder/struct.DeleteStatement.html#method.into_boxed + +* Update statements can now be boxed. This is useful for conditionally modifying + the where clause of a update statement. See [`UpdateStatement::into_boxed`] + for details. + +[`UpdateStatement::into_boxed`]: http://docs.diesel.rs/diesel/query_builder/struct.UpdateStatement.html#method.into_boxed + +* Added `order_by` as an alias for `order`. + +* Added `then_order_by`, which appends to an `ORDER BY` clause rather than + replacing it. This is useful with boxed queries to dynamically construct an + order by clause containing an unknown number of columns. + +* `#[derive(Insertable)]` can now work on structs with fields that implement + `Insertable` (meaning one field can map to more than one column). Add + `#[diesel(embed)]` to the field to enable this behavior. + +* Queries that treat a subselect as a single value (e.g. `foo = (subselect)`) + are now supported by calling [`.single_value()`]. + +* `#[derive(Insertable)]` implements now `Insertable` also on the struct itself, + not only on references to the struct + +[`.single_value()`]: http://docs.diesel.rs/diesel/query_dsl/trait.QueryDsl.html#method.single_value + +* `ConnectionError` now implements `PartialEq`. + +* Columns generated by `table!` now implement `Default` + +* `#[derive(AsChangeset)]` now implements `AsChangeset` on the struct itself, + and not only on a reference to the struct + +* Added support for deserializing `Numeric` into `BigDecimal` on SQLite. SQLite + has no arbitrary precision type, so the result will still have floating point + rounding issues. This is primarily to support things like `avg(int_col)`, + which we define as returning `Numeric` + +### Changed + +* The bounds on `impl ToSql for Cow<'a, T>` have been loosened to no longer + require that `T::Owned: ToSql`. + +* `32-column-tables` are now enabled by default. + +### Deprecated + +* `ne_any` has been renamed to `ne_all`. + +* The `large-tables` feature has been has been renamed to `32-column-tables`. + +* The `huge-tables` feature has been renamed to `64-column-tables`. + +* `IncompleteUpdateStatement` has been removed. Use `UpdateStatement` instead. + +### Fixed + +* `diesel database setup` now correctly handles database URLs containing query + strings + +* `diesel migration list` shows the proper migration order when mixing + old and new timestamp formats. (The migrations were always run in the correct + order, this only affects the display logic of `migration list`) + +* `#[derive(Identifiable)]` now correctly associates `#[primary_key]` with the + column name, not field name. + +* Select statements can no longer incorrectly appear in an expression context. + +* `exists` can no longer incorrectly receive values other than select + statements. + +* `MysqlConnection::establish` can now properly handle IPv6 addresses wrapped in + square brackets. + +### Jokes + +* Diesel is now powered by the blockchain because it's 2018. + +## [1.1.2] - 2018-04-05 + +* No changes + +## [1.1.1] - 2018-01-16 + +### Added + +* Added `diesel::r2d2::PoolError` as an alias for `r2d2::Error`. Previously this + type was inaccessible due to `diesel::r2d2::Error`. + +## [1.1.0] - 2018-01-15 + +### Added + +* `r2d2-diesel` has been merged into Diesel proper. You should no longer rely + directly on `r2d2-diesel` or `r2d2`. The functionality of both is exposed from + `diesel::r2d2`. + +* `r2d2::PooledConnection` now implements `Connection`. This means that you + should no longer need to write `&*connection` when using `r2d2`. + +* The `BINARY` column type name is now supported for SQLite. + +* The `QueryId` trait can now be derived. + +* `FromSqlRow` can now be derived for types which implement `FromSql`. + +* `AsExpression` can now be derived for types which implement `ToSql`. + +* `HasSqlType`, `NotNull`, and `SingleValue` can now be derived with + `#[derive(SqlType)]`. See the docs for those traits for more information. + +* The return type of `FromSql`, `FromSqlRow`, and `QueryableByName` can now be + written as `deserialize::Result`. + +* The return type of `ToSql` can now be written as `serialize::Result`. + +* Added support for SQLite's `INSERT OR IGNORE` and MySQL's `INSERT IGNORE` + via the `insert_or_ignore` function. + +* `min` and `max` can now be used with array expressions. + +* Added `diesel::dsl::array`, which corresponds to a PG `ARRAY[]` literal. + +* Added the `not_none!` macro, used by implementations of `FromSql` which do not + expect `NULL`. + +* Added `result::UnexpectedNullError`, an `Error` type indicating that an + unexpected `NULL` was received during deserialization. + +* Added `.or_filter`, which behaves identically to `.filter`, but using `OR` + instead of `AND`. + +* `helper_types` now contains a type for every method defined in + `expression_methods`, and every function in `dsl`. + +* Added `FromSql` impls for `*const str` and `*const [u8]` everywhere that + `String` and `Vec` are supported. These impls do not allocate, and are + intended for use by other impls which need to parse a string or bytes, and + don't want to allocate. These impls should never be used outside of another + `FromSql` impl. + +### Deprecated + +* *IMPORTANT NOTE* Due to [several][rust-deprecation-bug-1] + [bugs][rust-deprecation-bug-2] in Rust, many of the deprecations in this + release may not show a warning. If you want to ensure you are not using any + deprecated items, we recommend attempting to compile your code without the + `with-deprecated` feature by adding `default-features = false` to + `Cargo.toml`. + +[rust-deprecation-bug-1]: https://github.com/rust-lang/rust/issues/47236 +[rust-deprecation-bug-2]: https://github.com/rust-lang/rust/issues/47237 + +* Deprecated `impl_query_id!` in favor of `#[derive(QueryId)]` + +* Deprecated specifying a column name as `#[column_name(foo)]`. `#[column_name = + "foo"]` should be used instead. + +* The `types` module has been deprecated. It has been split into `sql_types`, + `serialize`, and `deserialize`. + +* `query_source::Queryable` and `query_source::QueryableByName` have been + deprecated. These traits have been moved to `deserialize`. + +* `backend::TypeMetadata` has been deprecated. It has been moved to `sql_types`. + +* `types::ToSqlOutput` has been deprecated. It has been renamed to + `serialize::Output`. + +* `helper_types::Not` is now `helper_types::not` + +### Fixed + +* `infer_schema!` generates valid code when run against a database with no + tables. + +## [1.0.0] - 2018-01-02 + +### Added + +* `#[derive(QueryableByName)]` can now handle structs that have no associated + table. If the `#[table_name]` annotation is left off, you must annotate each + field with `#[sql_type = "Integer"]` + +* `#[derive(QueryableByName)]` can now handle embedding other structs. To have a + field whose type is a struct which implements `QueryableByName`, rather than a + single column in the query, add the annotation `#[diesel(embed)]` + +* The `QueryDsl` trait encompasses the majority of the traits that were + previously in the `query_dsl` module. + +### Fixed + +* Executing select statements on SQLite will no longer panic when the database + returns `SQLITE_BUSY` + +* `table!`s which use the `Datetime` type with MySQL will now compile correctly, + even without the `chrono` feature enabled. + +* `#[derive(QueryableByName)]` will now compile correctly when there is a shadowed `Result` type in scope. + +* `BoxableExpression` can now be used with types that are not `'static` + +### Changed + +* `Connection::test_transaction` now requires that the error returned implement `Debug`. + +* `query_builder::insert_statement::InsertStatement` is now accessed as + `query_builder::InsertStatement` + +* `query_builder::insert_statement::UndecoratedInsertRecord` is now accessed as + `query_builder::UndecoratedInsertRecord` + +* `#[derive(QueryableByName)]` now requires that the table name be explicitly + stated. + +* Most of the traits in `query_dsl` have been moved to `query_dsl::methods`. + These traits are no longer exported in `prelude`. This should not affect most + apps, as the behavior of these traits is provided by `QueryDsl`. However, if + you were using these traits in `where` clauses for generic code, you will need + to explicitly do `use diesel::query_dsl::methods::WhateverDsl`. You may also + need to use UFCS in these cases. + +* If you have a type which implemented `QueryFragment` or `Query`, which you + intended to be able to call `execute` or `load` on, you will need to manually + implement `RunQueryDsl` for that type. The trait should be unconditionally + implemented (no where clause beyond what your type requires), and the body + should be empty. + +### Removed + +* All deprecated items have been removed. + +* `LoadDsl` and `FirstDsl` have been removed. Their functionality now lives in + `LoadQuery`. + +## [0.99.1] - 2017-12-01 + +### Changed + +* Diesel CLI now properly restricts its `clap` dependency. 0.99.0 mistakenly had + no upper bound on the version. + +## [0.99.0] - 2017-11-28 + +### Added + +* The `.for_update()` method has been added to select statements, allowing + construction of `SELECT ... FOR UPDATE`. + +* Added `insert_into(table).default_values()` as a replacement for + `insert_default_values()` + +* Added `insert_into(table).values(values)` as a replacement for + `insert(values).into(table)`. + +* Added support for MySQL's `REPLACE INTO` as `replace_into(table)`. + +* Added `replace_into(table).values(values)` as a replacement for + `insert_or_replace(values).into(table)`. + +* Added `on_conflict_do_nothing` on `InsertStatement` as a replacement for + `on_conflict_do_nothing` on `Insertable` structs. + +* Added `on_conflict` on `InsertStatement` as a replacement for + `on_conflict` on `Insertable` structs. + +* `filter` can now be called on update and delete statements. This means that + instead of `update(users.filter(...))` you can write + `update(users).filter(...)`. This allows line breaks to more naturally be + introduced. + +* Subselects can now reference columns from the outer table. For example, + `users.filter(exists(posts.filter(user_id.eq(users::id))))` will now compile. + +* `TextExpressionMethods` is now implemented for expressions of type + `Nullable` as well as `Text`. + +* `allow_tables_to_appear_in_same_query!` can now take more than 2 tables, and is the same + as invoking it separately for every combination of those tables. + +* Added `sql_query`, a new API for dropping to raw SQL that is more pleasant to + use than `sql` for complete queries. The main difference from `sql` is that + you do not need to state the return type, and data is loaded from the query by + name rather than by index. + +* Added a way to rename a table in the `table!` macro with `#[sql_name="the_table_name"]` + +* Added support for PostgreSQL's `DISTINCT ON`. See + [`.distinct_on()`][0.99.0-distinct-on] for more details + +### Changed + +* The signatures of `QueryId`, `Column`, and `FromSqlRow` have all changed to + use associated constants where appropriate. + +* You will now need to invoke `allow_tables_to_appear_in_same_query!` any time two tables + appear together in the same query, even if there is a `joinable!` invocation for those tables. + +* `diesel_codegen` should no longer explicitly be used as a dependency. Unless + you are using `infer_schema!` or `embed_migrations!`, you can simply remove it + from your `Cargo.toml`. All other functionality is now provided by `diesel` + itself. + +* Code using `infer_schema!` or `infer_table_from_schema!` must now add + `diesel_infer_schema` to `Cargo.toml`, and `#[macro_use] extern crate + diesel_infer_schema` to `src/lib.rs` + +* Code using `embed_migrations!` must now add `diesel_migrations` to `Cargo.toml`, + and `#[macro_use] extern crate diesel_migrations` to `src/lib.rs` + +* The `migrations` module has been moved out of `diesel` and into + `diesel_migrations` + +### Deprecated + +* Deprecated `insert_default_values()` in favor of + `insert_into(table).default_values()` + +* Deprecated `insert(values).into(table)` in favor of + `insert_into(table).values(values)`. + +* Deprecated `insert_or_replace(values).into(table)` in favor of + `replace_into(table).values(values)`. + +* Deprecated `.values(x.on_conflict_do_nothing())` in favor of + `.values(x).on_conflict_do_nothing()` + +* Deprecated `.values(x.on_conflict(y, do_nothing()))` in favor of + `.values(x).on_conflict(y).do_nothing()` + +* Deprecated `.values(x.on_conflict(y, do_update().set(z)))` in favor of + `.values(x).on_conflict(y).do_update().set(z)` + +* Deprecated `enable_multi_table_joins` in favor of + `allow_tables_to_appear_in_same_query!` + +* Deprecated `SqlLiteral#bind`. `sql` is intended for use with small fragments + of SQL, not complete queries. Writing bind parameters in raw SQL when you are + not writing the whole query is error-prone. Use `sql_query` if you need raw + SQL with bind parameters. + +### Removed + +* `IntoInsertStatement` and `BatchInsertStatement` have been removed. It's + unlikely that your application is using these types, but `InsertStatement` is + now the only "insert statement" type. + +* `Citext` as a type alias for `Text` has been removed. Writing + `citext_column.eq("foo")` would perform a case-sensitive comparison. More + fleshed out support will be required. + +### Fixed + +* When using MySQL and SQLite, dates which cannot be represented by `chrono` + (such as `0000-00-00`) will now properly return an error instead of panicking. + +* MySQL URLs will now properly percent decode the username and password. + +* References to types other than `str` and slice can now appear on structs which + derive `Insertable` or `AsChangeset`. + +* Deserializing a date/time/timestamp column into a chrono type on SQLite will + now handle any value that is in a format documented as valid for SQLite's + `strftime` function except for the string `'now'`. + +[0.99.0-distinct-on]: http://docs.diesel.rs/diesel/query_dsl/trait.DistinctOnDsl.html#tymethod.distinct_on + +## [0.16.0] - 2017-08-24 + +### Added + +* Added helper types for inner join and left outer join + +* `diesel::debug_query` has been added as a replacement for `debug_sql!`. This + function differs from the macro by allowing you to specify the backend, and + will generate the actual query which will be run. The returned value will + implement `Display` and `Debug` to show the query in different ways + +* `diesel::pg::PgConnection`, `diesel::mysql::MysqlConnection`, and + `diesel::sqlite::SqliteConnection` are now exported from `diesel::prelude`. + You should no longer need to import these types explicitly. + +* Added support for the Decimal datatype on MySQL, using the [BigDecimal crate][bigdecimal-0.16.0]. + +* Added support for the [Range][range-0.16.0] type on postgreSQL. + +* Added support for the Datetime type on MySQL. + +* Added support for the Blob type on MySQL. + +* `infer_schema!` will now automatically detect which tables can be joined based + on the presence of foreign key constraints. + +* Added support for `Add` and `Sub` to timestamp types. + +* Added a way to rename columns in the table macro with `#[sql_name="the_column_name"]` + +* Schema inference now also generates documentation comments for tables and + columns. For `infer_schema!`, this is enabled by default. If you are using + Diesel's CLI tool, pass the new `--with-docs` parameter: + `diesel print-schema --with-docs`. + +* `infer_schema!` now automatically renames columns that conflict with + a Rust keyword by placing a _ at the end of the name. For example, + a column called `type` will be referenced as `type_` in Rust. + +### Changed + +* The deprecated `debug_sql!` and `print_sql!` functions will now generate + backend specific SQL. (The specific backend they will generate for will be + arbitrarily chosen based on the backends enabled). + +* `#[belongs_to]` will no longer generate the code required to join between two + tables. You will need to explicitly invoke `joinable!` instead, unless you are + using `infer_schema!` + +* Changed the migration directory name format to `%Y-%m-%d-%H%M%S`. + +* `between` and `not_between` now take two arguments, rather than a range. + +### Removed + +* `debug_sql!` has been deprecated in favor of `diesel::debug_query`. + +* `print_sql!` has been deprecated without replacement. + +* `diesel::backend::Debug` has been removed. + +### Fixed + +* Diesel now properly supports joins in the form: + `grandchild.join(child.join(parent))`. Previously only + `parent.join(child.join(grandchild))` would compile. + +* When encoding a `BigDecimal` on PG, `1.0` is no longer encoded as if it were + `1`. + +[bigdecimal-0.16.0]: https://crates.io/crates/bigdecimal +[range-0.16.0]: https://docs.diesel.rs/diesel/pg/types/sql_types/struct.Range.html + +## [0.15.2] - 2017-07-28 + +### Fixed + +* `BigDecimal` now properly encodes numbers starting with `10000` on postgres. + See [issue #1044][] for details. + +[issue #1044]: https://github.com/diesel-rs/diesel/issues/1044 + +## [0.15.1] - 2017-07-24 + +* No changes to public API + +## [0.15.0] - 2017-07-23 + +### Added + +* Added support for the PG `IS DISTINCT FROM` operator + +* The `ON` clause of a join can now be manually specified. See [the + docs][join-on-dsl-0.15.0] for details. + +[join-on-dsl-0.15.0]: https://docs.diesel.rs/diesel/prelude/trait.JoinOnDsl.html#method.on + +### Changed + +* Diesel will now automatically invoke `numeric_expr!` for your columns in the + common cases. You will likely need to delete any manual invocations of this + macro. + +* `Insertable` no longer treats all fields as nullable for type checking. What + this means for you is that if you had an impl like `impl + AsExpression, DB> for CustomType` in your code base, you can + remove the `Nullable` portion (Unless you are using it with fields that are + actually nullable) + +* Connections will now explicitly set the session time zone to UTC when the + connection is established + +## [0.14.1] - 2017-07-10 + +### Changed + +* The return type of `sum` and `avg` is now always considered to be `Nullable`, + as these functions return `NULL` when against on an empty table. + +## [0.14.0] - 2017-07-04 + +### Added + +* Added support for joining between more than two tables. The query builder can + now be used to join between any number of tables in a single query. See the + documentation for [`JoinDsl`][join-dsl-0.14.0] for details + +[join-dsl-0.14.0]: https://docs.diesel.rs/diesel/prelude/trait.JoinDsl.html + +* Added support for the [PostgreSQL network types][pg-network-0.14.0] `MACADDR`. + +* Added support for the Numeric datatypes, using the [BigDecimal crate][bigdecimal-0.14.0]. + +* Added a function which maps to SQL `NOT`. See [the docs][not-0.14.0] for more + details. + +* Added the [`insert_default_values`][insert-default-0.14.0] function. + +[pg-network-0.14.0]: https://www.postgresql.org/docs/9.6/static/datatype-net-types.html +[not-0.14.0]: https://docs.diesel.rs/diesel/expression/dsl/fn.not.html +[insert-default-0.14.0]: https://docs.diesel.rs/diesel/fn.insert_default_values.html +[bigdecimal-0.14.0]: https://crates.io/crates/bigdecimal + +* Added `diesel_prefix_operator!` which behaves identically to + `diesel_postfix_operator!` (previously `postfix_predicate!`), but for + operators like `NOT` which use prefix notation. + +### Changed + +* `infix_predicate!` and `infix_expression!` have been renamed to + `diesel_infix_operator!`. + +* `postfix_predicate!` and `postfix_expression!` have been renamed to + `diesel_postfix_operator!`. + +* Trait bounds along the lines of `T: LoadDsl, U: Queryable` should be changed to `T: LoadQuery`. + +* Diesel now uses a migration to set up its timestamp helpers. To generate this + migration for your project, run `diesel database setup`. + +### Removed + +* `#[has_many]` has been removed. Its functionality is now provided by + `#[belongs_to]` on the child struct. If there is no child struct to + put `#[belongs_to]` on, you can invoke `joinable!` directly instead. + +## [0.13.0] - 2017-05-15 + +### Added + +* Added support for chrono types with SQLite. + +* Bind values can now be supplied to queries constructed using raw SQL. See [the + docs][sql-bind-0.13.0] for more details. + +[sql-bind-0.13.0]: https://docs.diesel.rs/diesel/expression/sql_literal/struct.SqlLiteral.html#method.bind + +* Added support for the [PostgreSQL network types][pg-network-0.13.0] `CIDR` and + `INET`. + +[pg-network-0.13.0]: https://www.postgresql.org/docs/9.6/static/datatype-net-types.html + +* Added support for `ILIKE` in PostgreSQL. + +* `diesel migration list` will show all migrations, marking those that have been + run. + +* `diesel migration pending` will list any migrations which have not been run. + +* Added support for numeric operations with nullable types. + +* Added [`migrations::any_pending_migrations`][pending-migrations-0.13.0]. + +[pending-migrations-0.13.0]: https://docs.diesel.rs/diesel/migrations/fn.any_pending_migrations.html + +### Fixed + +* Diesel CLI now respects the `--migration-dir` argument or the + `MIGRATION_DIRECTORY` environment variable for all commands. + +* Diesel CLI now properly escapes the database name. + +## [0.12.1] - 2017-05-07 + +### Changed + +* Locked the chrono dependency to require exactly `0.3.0` instead of a semver + restriction. This restriction is required for the 0.12 line of releases to + continue compiling, as the chrono project is including breaking changes in + patch releases. + +## [0.12.0] - 2017-03-16 + +### Added + +* Added support for the majority of PG upsert (`INSERT ON CONFLICT`). We now + support specifying the constraint, as well as `DO UPDATE` in addition to `DO + NOTHING`. See [the module docs][upsert-0.12.0] for details. + +[upsert-0.12.0]: https://docs.diesel.rs/diesel/pg/upsert/index.html + +* Added support for the SQL concatenation operator `||`. See [the docs for + `.concat`][concat-0.12.0] for more details. + +[concat-0.12.0]: https://docs.diesel.rs/diesel/expression/expression_methods/text_expression_methods/trait.TextExpressionMethods.html#method.concat + +* Added support for the PostgreSQL [`Money` type][pg-money-0.12.0]. + +[pg-money-0.12.0]: https://www.postgresql.org/docs/9.6/static/datatype-money.html + +* Diesel CLI: Added `db` as an alias for `database`, so you can now write `diesel db setup` (which is almost 40% faster!). + +* The `table!` macro now allows you to use types from crates outside of Diesel. + You can specify where types should be imported from by doing: `table! { use + some_modules::*; foo { columns... }`. Not specifying any any modules is + equivalent to `use diesel::types::*;`. + +### Fixed + +* `diesel_codegen` will provide a more useful error message when it encounters + an unsupported type that contains a space in MySQL. + +* `#[derive(AsChangeset)]` will now respect custom `#[primary_key]` annotations, + and avoid setting those columns. + +### Removed + +* `WithDsl` and `Aliased` have been removed. They were a feature that was + actually closer to a cross join than the names implied, and wasn't fully + thought out. The functionality they provided will return as joins are further + revamped. + +* The internal use macro `select_column_workaround!` has been removed. If you + were relying on this internal macro, you can simply delete the line that was + calling it. + +* Columns from the right side of a left join will now need to have `.nullable()` + explicitly called to be passed to `.select`. This allows it to compose better + with functions that don't normally take nullable columns (e.g. + `lower(name).nullable()`). + +## [0.11.4] - 2017-02-21 + +### Fixed + +* Corrected a memory safety violation when using MySQL. + +## 0.11.3 - 2017-02-21 + +* No changes + +## [0.11.2] - 2017-02-19 + +### Changed + +* `pq-sys` and `mysqlclient-sys` will no longer attempt to generate bindings at + compile time. Generating the bindings required a bleeding edge version of + clang, which caused too many issues. + +## [0.11.1] - 2017-02-17 + +### Fixed + +* `.on_conflict_do_nothing()` now interacts with slices properly. + +* `MysqlConnection` now implements `Send`, which is required for connection + pooling. + +## [0.11.0] - 2017-02-16 + +### Added + +* Added support for MySQL as an additional backend. Diesel CLI will install with + MySQL support by default. To enable it for Diesel and Diesel Codegen, add + `features = ["mysql"]` to Cargo.toml. See [the docs][mysql-0.11.0] for details. + +[mysql-0.11.0]: https://docs.diesel.rs/diesel/mysql/index.html + +* Added support for PG's `ON CONFLICT DO NOTHING` clause. See [the + docs][on-conflict-0.11.0] for details. + +[on-conflict-0.11.0]: https://docs.diesel.rs/diesel/pg/upsert/trait.OnConflictExtension.html#method.on_conflict_do_nothing + +* Queries constructed using [`diesel::select`][select-0.11.0] now work properly + when [boxed][boxed-0.11.0]. + +[select-0.11.0]: https://docs.rs/diesel/0.11.0/diesel/fn.select.html +[boxed-0.11.0]: https://docs.rs/diesel/0.11.0/prelude/trait.BoxedDsl.html + +* Arrays containing null are now supported. `infer_schema!` will never infer an + array that contains null, but a `table!` definition which specifies a type of + `Array>` can now be deserialized to `Vec>` + +* [`#[belongs_to]`][belongs-to-0.11.0] associations can now be self referential. + This will generate the code required for + [`belonging_to`][belonging-to-0.11.0], without generating code for performing + a join. + +[belongs-to-0.11.0]: https://docs.rs/diesel/0.11.0/diesel/associations/trait.BelongsTo.html +[belonging-to-0.11.0]: https://docs.rs/diesel/0.11.0/diesel/prelude/trait.BelongingToDsl.html#tymethod.belonging_to + +* Added support for the `rust-lang-deprecated/time` crate on PostgreSQL. To use + it, add `features = ["deprecated-time"]` + +### Changed + +* It is no longer possible to exhaustively match against + `result::ConnectionError`. + +* Updated chrono to version 0.3. + +* [`max`][max-0.11.0] and [`min`][min-0.11.0] are now always nullable. The database will + return `NULL` when the table is empty. + +[max-0.11.0]: https://docs.diesel.rs/diesel/expression/dsl/fn.max.html +[min-0.11.0]: https://docs.diesel.rs/diesel/expression/dsl/fn.min.html + +* [`now`][now-0.11.0] can now be used as an expression of type `Timestamptz`. + +[now-0.11.0]: https://docs.diesel.rs/diesel/expression/dsl/struct.now.html + +* [`Connection::transaction`][transaction-0.11.0] now returns your error + directly instead of wrapping it in `TransactionError`. It requires that the + error implement `From` + +[transaction-0.11.0]: https://docs.diesel.rs/diesel/connection/trait.Connection.html#method.transaction + +* The way tuples of columns from the right side of left outer joins interact + with `.select` has changed. If you are deserializing into an option of a tuple + (instead of a tuple of options), you will need to explicitly call + `.nullable()`. (e.g. `.select(users::name, (posts::title, + posts::body).nullable())`) + +### Removed + +* `result::TransactionError` +* `result::TransactionResult` + +## [0.10.1] - 2017-02-08 + +### Fixed + +* `infer_table_from_schema!` properly handles table names with a custom schema + specified. + +### Changed + +* Updated uuid to version 0.4. + +## [0.10.0] - 2017-02-02 + +### Added + +* Added support for the PostgreSQL [`json` and `jsonb` types][pg-json]. They can + be mapped to/from `serde_json::Value`. The `serde` feature must be enabled to + use the JSON types. + +[pg-json]: https://www.postgresql.org/docs/9.6/static/datatype-json.html + +* Added the `print-schema` command to Diesel CLI. This command will print the + output of the `infer_schema!` macro. For more information run `diesel help + print-schema`. + +### Changed + +* When possible, we will use deprecation warnings for breaking changes. + Deprecated code requires the `with-deprecated` feature, which is enabled by + default. + +* The `postgres` feature is no longer enabled by default by `diesel` or + `diesel_codegen_syntex`. Add `features = ["postgres"]` to your `Cargo.toml`. + +* The `persistable` module has been renamed to `insertable`. + +### Fixed + +* `#[derive(Insertable)]` allows fields of type `Option` to be used with + columns that are not null if they have a default value. + +### Removed + +* `diesel_codegen_syntex` is no longer supported. `diesel_codegen` can now be + used on stable Rust. + +* Dropped support for Rust 1.14 and earlier + +## [0.9.1] - 2016-12-09 + +### Fixed + +* Added missing impls for loading `chrono::NaiveDateTime` from a column of type + `Timestamptz` + +* `#[derive(AsChangeset)]` no longer assumes that `use diesel::prelude::*` has + been done. + +* `debug_sql!` can now properly be used with types from `chrono` or + `std::time`. + +* When using PostgreSQL, attempting to get the error message of a query which + could not be transmitted to the server (such as a query with greater than + 65535 bind parameters) will no longer panic. + +## [0.9.0] - 2016-12-08 + +### Added + +* Added support for SQL `NOT IN` using the `ne_any` method. + +* The `table!` macro now allows custom schemas to be specified. Example: + + ```rust + table! { + schema_1.table_1 { + id -> Integer, + } + } + ``` + + The generated module will still be called `table_1`. + +* The `infer_table_from_schema!` macro now allows custom schemas to be + specified. Example: + + ```rust + infer_table_from_schema!("dotenv:DATABASE_URL", "schema_1.table_1"); + ``` + +* The `infer_schema!` optionally allows a schema name as the second argument. Any + schemas other than `public` will be wrapped in a module with the same name as + the schema. For example, `schema_1.table_1` would be referenced as + `schema_1::table_1`. + +* Added support for batch insert on SQLite. This means that you can now pass a + slice or vector to [`diesel::insert`][insert] on all backends. + +[insert]: https://docs.diesel.rs/diesel/fn.insert.html + +* Added a function for SQL `EXISTS` expressions. See + [`diesel::expression::dsl::exists`][exists] for details. + +[exists]: https://docs.diesel.rs/diesel/expression/dsl/fn.sql.html + +* `#[derive(Identifiable)]` can be used with structs that have primary keys + other than `id`, as well as structs with composite primary keys. You can now + annotate the struct with `#[primary_key(nonstandard)]` or `#[primary_key(foo, + bar)]`. + +### Changed + +* All macros with the same name as traits we can derive (e.g. `Queryable!`) have + been renamed to `impl_Queryable!` or similar. + +### Fixed + +* `#[derive(Identifiable)]` now works on structs with lifetimes + +* Attempting to insert an empty slice will no longer panic. It does not execute + any queries, but the result will indicate that we successfully inserted 0 + rows. + +* Attempting to update a record with no changes will no longer generate invalid + SQL. The result of attempting to execute the query will still be an error, but + but it will be a `Error::QueryBuilderError`, rather than a database error. + This means that it will not abort the current transaction, and can be handled + by applications. + +* Calling `eq_any` or `ne_any` with an empty array no longer panics. + `eq_any(vec![])` will return no rows. `ne_any(vec![])` will return all rows. + +## [0.8.2] - 2016-11-22 + +### Changed + +* Fixed support for nightlies later than 2016-11-07 + +* Removed support for nightlies earlier than 2016-11-07 + +* Calls to `infer_table_from_schema!` will need to be wrapped in a module if + called more than once. This change is to work around further limitations of + the Macros 1.1 system. Example: + + ```rust + mod infer_users { + infer_table_from_schema!("dotenv:DATABASE_URL", "users"); + } + pub use self::infer_users::*; + ``` + +## [0.8.1] - 2016-11-01 + +### Added + +* SQLite date and time columns can be deserialized to/from strings. + +### Fixed + +* Fixed an issue with `diesel_codegen` on nightlies >= 2016-10-20 + +## [0.8.0] - 2016-10-10 + +### Added + +* Added partial support for composite primary keys. + +* Added support for PostgreSQL `NULLS FIRST` and `NULLS LAST` when sorting. + See https://docs.diesel.rs/diesel/prelude/trait.SortExpressionMethods.html + for details. + +* Added support for the `timestamp with time zone` type in PostgreSQL (referred + to as `diesel::types::Timestamptz`) + +* Diesel CLI can now generate bash completion. See [the readme][bash completion] + for details. + +* `infer_schema!` and `infer_table_from_schema!` can now take `"env:foo"` + instead of `env!("foo")` and `"dotenv:foo"` instead of `dotenv!("foo")`. The + use of `dotenv` requires the `dotenv` feature on `diesel_codegen`, which is + included by default. Using `env!` and `dotenv!` will no longer work with + `diesel_codegen`. They continue to work with `diesel_codegen_syntex`, but that + crate will be deprecated when Macros 1.1 is in the beta channel for Rust. + +[bash completion]: https://github.com/diesel-rs/diesel/blob/b1a0d9901f0f2a8c8d530ccba8173b57f332b891/diesel_cli/README.md#bash-completion + +### Changed + +* Structs annotated with `#[has_many]` or `#[belongs_to]` now require + `#[derive(Associations)]`. This is to allow them to work with Macros 1.1. + +* `embed_migrations!` now resolves paths relative to `Cargo.toml` instead of the + file the macro was called from. This change is required to allow this macro to + work with Macros 1.1. + +### Fixed + +* `diesel migrations run` will now respect migration directories overridden by + command line argument or environment variable +* The `infer_schema!` macro will no longer fetch views alongside with tables. + This was a source of trouble for people that had created views or are using + any extension that automatically creates views (e.g. PostGIS) + +### Changed + +* `#[changeset_for(foo)]` should now be written as + `#[derive(AsChangeset)] #[table_name="foo"]`. If you were specifying + `treat_none_as_null = "true"`, you should additionally have + `#[changeset_options(treat_none_as_null = "true")]`. +* `#[insertable_into(foo)]` should now be written as + `#[derive(Insertable)] #[table_name="foo"]`. + +## [0.7.2] - 2016-08-20 + +* Updated nightly version and syntex support. + +## [0.7.1] - 2016-08-11 + +### Changed + +* The `Copy` constraint has been removed from `Identifiable::Id`, and + `Identifiable#id` now returns `&Identifiable::Id`. + +### Fixed + +* `#[belongs_to]` now respects the `foreign_key` option when using + `diesel_codegen` or `diesel_codegen_syntex`. + +## [0.7.0] - 2016-08-01 + +### Added + +* The initial APIs have been added in the form of `#[has_many]` and + `#[belongs_to]`. See [the module documentation][associations-module] for more + information. + +* The `Insertable!` macro can now be used instead of `#[insertable_into]` for + those wishing to avoid syntax extensions from `diesel_codegen`. See + https://docs.diesel.rs/diesel/macro.Insertable!.html for details. + +* The `Queryable!` macro can now be used instead of `#[derive(Queryable)]` for + those wishing to avoid syntax extensions from `diesel_codegen`. See + https://docs.diesel.rs/diesel/macro.Queryable!.html for details. + +* The `Identifiable!` macro can now be used instead of `#[derive(Identifiable)]` for + those wishing to avoid syntax extensions from `diesel_codegen`. See + https://docs.diesel.rs/diesel/macro.Identifiable!.html for details. + +* The `AsChangeset!` macro can now be used instead of `#[changeset_for(table)]` + for those wishing to avoid syntax extensions from `diesel_codegen`. See + https://docs.diesel.rs/diesel/macro.AsChangeset!.html for details. + +* Added support for the PostgreSQL `ALL` operator. See + https://docs.diesel.rs/diesel/pg/expression/dsl/fn.all.html for details. + +* Added support for `RETURNING` expressions in `DELETE` statements. Implicitly + these queries will use `RETURNING *`. + +### Changed + +* Diesel now targets `nightly-2016-07-07`. Future releases will update to a + newer nightly version on the date that Rust releases. + +* `diesel_codegen` has been split into two crates. `diesel_codegen` and + `diesel_codegen_syntex`. See [this commit][syntex-split] for migration + information. + +* Most structs that implement `Queryable` will now also need + `#[derive(Identifiable)]`. + +* `infer_schema!` on SQLite now accepts a larger range of type names + +* `types::VarChar` is now an alias for `types::Text`. Most code should be + unaffected by this. PG array columns are treated slightly differently, + however. If you are using `varchar[]`, you should switch to `text[]` instead. + +* Struct fields annotated with `#[column_name="name"]` should be changed to + `#[column_name(name)]`. + +* The structure of `DatabaseError` has changed to hold more information. See + https://docs.diesel.rs/diesel/result/enum.Error.html and + https://docs.diesel.rs/diesel/result/trait.DatabaseErrorInformation.html for + more information + +* Structs which implement `Identifiable` can now be passed to `update` and + `delete`. This means you can now write `delete(&user).execute(&connection)` + instead of `delete(users.find(user.id)).execute(&connection)` + +[associations-module]: https://docs.diesel.rs/diesel/associations/index.html +[syntex-split]: https://github.com/diesel-rs/diesel/commit/36b8801bf5e9594443743e6a7c62e29d3dce36b7 + +### Fixed + +* `&&[T]` can now be used in queries. This allows using slices with things like + `#[insertable_into]`. + +## [0.6.1] 2016-04-14 + +### Added + +* Added the `escape` method to `Like` and `NotLike`, to specify the escape + character used in the pattern. See [EscapeExpressionMethods][escape] for + details. + +[escape]: https://docs.diesel.rs/diesel/expression/expression_methods/escape_expression_methods/trait.EscapeExpressionMethods.html + +### Fixed + +* `diesel_codegen` and `diesel_cli` now properly rely on Diesel 0.6.0. The + restriction to 0.5.0 was an oversight. + +* `infer_schema!` now properly excludes metadata tables on SQLite. + +* `infer_schema!` now properly maps types on SQLite. + +## [0.6.0] 2016-04-12 + +### Added + +* Queries can now be boxed using the `into_boxed()` method. This is useful for + conditionally modifying queries without changing the type. See + [BoxedDsl][boxed_dsl] for more details. + +* `infer_schema!` is now supported for use with SQLite3. + +* The maximum table size can be increased to 52 by enabling the `huge-tables` + feature. This feature will substantially increase compile times. + +* The `DISTINCT` keyword can now be added to queries via the `distinct()` + method. + +* `SqliteConnection` now implements `Send` + +[boxed_dsl]: https://docs.diesel.rs/diesel/prelude/trait.BoxedDsl.html + +### Changed + +* `diesel::result::Error` now implements `Send` and `Sync`. This required a + change in the return type of `ToSql` and `FromSql` to have those bounds as + well. + +* It is no longer possible to pass an owned value to `diesel::insert`. `insert` + will now give a more helpful error message when you accidentally try to pass + an owned value instead of a reference. + +### Fixed + +* `#[insertable_into]` can now be used with structs that have lifetimes with + names other than `'a'`. + +* Tables with a single column now properly return a single element tuple. E.g. + if the column was of type integer, then `users::all_columns` is now `(id,)` + and not `id`. + +* `infer_schema!` can now work with tables that have a primary key other than + `id`. + +### Removed + +* Removed the `no select` option for the `table!` macro. This was a niche + feature that didn't fit with Diesel's philosophies. You can write a function + that calls `select` for you if you need this functionality. + +## [0.5.4] 2016-03-23 + +* Updated `diesel_codegen` to allow syntex versions up to 0.30.0. + +## [0.5.3] 2016-03-12 + +### Added + +* Added helper function `diesel_manage_updated_at('TABLE_NAME')` to postgres + upon database setup. This function sets up a trigger on the specified table + that automatically updates the `updated_at` column to the `current_timestamp` + for each affected row in `UPDATE` statements. + +* Added support for explicit `RETURNING` expressions in `INSERT` and `UPDATE` + queries. Implicitly these queries will still use `RETURNING *`. + +### Fixed + +* Updated to work on nightly from early March + +## [0.5.2] 2016-02-27 + +* Updated to work on nightly from late February + +## [0.5.1] 2016-02-11 + +* Diesel CLI no longer has a hard dependency on SQLite and PostgreSQL. It + assumes both by default, but if you need to install on a system that doesn't + have one or the other, you can install it with `cargo install diesel_cli + --no-default-features --features postgres` or `cargo install diesel_cli + --no-default-features --features sqlite` + +## [0.5.0] 2016-02-05 + +### Added + +* Added support for SQLite. Diesel still uses postgres by default. To use SQLite + instead, add `default-features = false, features = ["sqlite"]` to your + Cargo.toml. You'll also want to add `default-features = false, features = + ["sqlite"]` to `diesel_codegen`. + Since SQLite is a much more limited database, it does not support our full set + of features. You can use SQLite and PostgreSQL in the same project if you + desire. + +* Added support for mapping `types::Timestamp`, `types::Date`, and `types::Time` + to/from `chrono::NaiveDateTime`, `chrono::NaiveDate`, and `chrono::NaiveTime`. + Add `features = ["chrono"]` to enable. + +* Added a `treat_none_as_null` option to `changeset_for`. When set to `true`, + a model will set a field to `Null` when an optional struct field is `None`, + instead of skipping the field entirely. The default value of the option is + `false`, as we think the current behavior is a much more common use case. + +* Added `Expression#nullable()`, to allow comparisons of not null columns with + nullable ones when required. + +* Added `sum` and `avg` functions. + +* Added the `diesel setup`, `diesel database setup`, and `diesel database + reset` commands to the CLI. + +* Added support for SQL `IN` statements through the `eq_any` method. + +* Added a top level `select` function for select statements with no from clause. + This is primarily intended to be used for testing Diesel itself, but it has + been added to the public API as it will likely be useful for third party + crates in the future. `select(foo).from(bar)` might be a supported API in the + future as an alternative to `bar.select(foo)`. + +* Added `expression::dsl::sql` as a helper function for constructing + `SqlLiteral` nodes. This is primarily intended to be used for testing Diesel + itself, but is part of the public API as an escape hatch if our query builder + DSL proves inadequate for a specific case. Use of this function in any + production code is discouraged as it is inherently unsafe and avoids real type + checking. + +### Changed + +* Moved most of our top level trait exports into a prelude module, and + re-exported our CRUD functions from the top level. + `diesel::query_builder::update` and friends are now `diesel::update`, and you + will get them by default if you import `diesel::*`. For a less aggressive + glob, you can import `diesel::prelude::*`, which will only export our traits. + +* `Connection` is now a trait instead of a struct. The struct that was + previously known as `Connection` can be found at `diesel::pg::PgConnection`. + +* Rename both the `#[derive(Queriable)]` attribute and the `Queriable` trait to + use the correct spelling `Queryable`. + +* `load` and `get_results` now return a `Vec` instead of an iterator. + +* Replaced `Connection#find(source, id)` with + `source.find(id).first(&connection)`. + +* The `debug_sql!` macro now uses \` for identifier quoting, and `?` for bind + parameters, which is closer to a "generic" backend. The previous behavior had + no identifier quoting, and used PG specific bind params. + +* Many user facing types are now generic over the backend. This includes, but is + not limited to `Queryable` and `Changeset`. This change should not have much + impact, as most impls will have been generated by diesel_codegen, and that API + has not changed. + +* The mostly internal `NativeSqlType` has been removed. It now requires a known + backend. `fn foo() where T: NativeSqlType` is now `fn foo() where + DB: HasSqlType` + +### Removed + +* `Connection#query_sql` and `Connection#query_sql_params` have been removed. + These methods were not part of the public API, and were only meant to be used + for testing Diesel itself. However, they were technically callable from any + crate, so the removal has been noted here. Their usage can be replaced with + bare `select` and `expression::dsl::sql`. + +## [0.4.1] 2016-01-11 + +### Changed + +* Diesel CLI will no longer output notices about `__diesel_schema_migrations` + already existing. + +* Relicensed under MIT/Apache dual + +## [0.4.0] 2016-01-08 + +### Added + +* Added Diesel CLI, a tool for managing your schema. + See [the readme](https://github.com/diesel-rs/diesel/blob/v0.4.0/README.md#database-migrations) + for more information. + +* Add the ability for diesel to maintain your schema for you automatically. See + the [migrations](https://docs.diesel.rs/diesel/migrations/index.html) + module for individual methods. + +* Add DebugQueryBuilder to build sql without requiring a connection. + +* Add print_sql! and debug_sql! macros to print out and return sql strings from + QueryFragments. + +### Fixed + +* `#[changeset_for]` can now be used with structs containing a `Vec`. Fixes + [#63](https://github.com/diesel-rs/diesel/issues/63). + +* No longer generate invalid SQL when an optional update field is not the first + field on a changeset. Fixes [#68](https://github.com/diesel-rs/diesel/issues/68). + +* `#[changeset_for]` can now be used with structs containing only a single field + other than `id`. Fixes [#66](https://github.com/diesel-rs/diesel/issues/66). + +* `infer_schema!` properly works with array columns. Fixes + [#65](https://github.com/diesel-rs/diesel/issues/65). + +## [0.3.0] 2015-12-04 + +### Changed + +* `#[changeset_for(table)]` now treats `Option` fields as an optional update. + Previously a field with `None` for the value would insert `NULL` into the + database field. It now does not update the field if the value is `None`. + +* `.save_changes` (generated by `#[changeset_for]`) now returns a new struct, + rather than mutating `self`. The returned struct can be any type that + implements `Queryable` for the right SQL type + +### Fixed + +* `#[derive(Queryable)]` now allows generic parameters on the struct. + +* Table definitions can now support up to 26 columns. Because this increases our + compile time by 3x, `features = ["large-tables"]` is needed to support table + definitions above 16 columns. + +### Added + +* Quickcheck is now an optional dependency. When `features = ["quickcheck"]` is + added to `Cargo.toml`, you'll gain `Arbitrary` implementations for everything + in `diesel::data_types`. + +* Added support for the SQL `MIN` function. + +* Added support for the `Numeric` data type. Since there is no Big Decimal type + in the standard library, a dumb struct has been provided which mirrors what + Postgres provides, which can be converted into whatever crate you are using. + +* Timestamp columns can now be used with `std::time::SystemTime` when compiled + with `--features unstable` + +* Implemented `Send` on `Connection` (required for R2D2 support) + +* Added `infer_schema!` and `infer_table_from_schema!`. Both macros take a + database URL, and will invoke `table!` for you automatically based on the + schema. `infer_schema!` queries for the table names, while + `infer_table_from_schema!` takes a table name as the second argument. + +## [0.2.0] - 2015-11-30 + +### Added + +* Added an `execute` method to `QueryFragment`, which is intended to replace + `Connection#execute_returning_count`. The old method still exists for use + under the hood, but has been hidden from docs and is not considered public + API. + +* Added `get_result` and `get_results`, which work similarly to `load` and + `first`, but are intended to make code read better when working with commands + like `create` and `update`. In the future, `get_result` may also check that + only a single row was affected. + +* Added [`insert`][insert], which mirrors the pattern of `update` and `delete`. + +### Changed + +* Added a hidden `__Nonexhaustive` variant to `result::Error`. This is not + intended to be something you can exhaustively match on, but I do want people + to be able to check for specific cases, so `Box` is + not an option. + +* `query_one`, `find`, and `first` now assume a single row is returned. For + cases where you actually expect 0 or 1 rows to be returned, the `optional` + method has been added to the result, in case having a `Result>` is + more idiomatic than checking for `Err(NotFound)`. + +### Deprecated + +* `Connection#insert` and `Connection#insert_returning_count` have been + deprecated in favor of [`insert`][insert] + +## 0.1.0 - 2015-11-29 + +* Initial release + +[0.2.0]: https://github.com/diesel-rs/diesel/compare/v0.1.0...v0.2.0 +[0.3.0]: https://github.com/diesel-rs/diesel/compare/v0.2.0...v0.3.0 +[0.4.0]: https://github.com/diesel-rs/diesel/compare/v0.3.0...v0.4.0 +[0.4.1]: https://github.com/diesel-rs/diesel/compare/v0.4.0...v0.4.1 +[0.5.0]: https://github.com/diesel-rs/diesel/compare/v0.4.1...v0.5.0 +[0.5.1]: https://github.com/diesel-rs/diesel/compare/v0.5.0...v0.5.1 +[0.5.2]: https://github.com/diesel-rs/diesel/compare/v0.5.1...v0.5.2 +[0.5.3]: https://github.com/diesel-rs/diesel/compare/v0.5.2...v0.5.3 +[0.5.4]: https://github.com/diesel-rs/diesel/compare/v0.5.3...v0.5.4 +[0.6.0]: https://github.com/diesel-rs/diesel/compare/v0.5.4...v0.6.0 +[0.6.1]: https://github.com/diesel-rs/diesel/compare/v0.6.0...v0.6.1 +[0.7.0]: https://github.com/diesel-rs/diesel/compare/v0.6.1...v0.7.0 +[0.7.1]: https://github.com/diesel-rs/diesel/compare/v0.7.0...v0.7.1 +[0.7.2]: https://github.com/diesel-rs/diesel/compare/v0.7.1...v0.7.2 +[0.8.0]: https://github.com/diesel-rs/diesel/compare/v0.7.2...v0.8.0 +[0.8.1]: https://github.com/diesel-rs/diesel/compare/v0.8.0...v0.8.1 +[0.8.2]: https://github.com/diesel-rs/diesel/compare/v0.8.1...v0.8.2 +[0.9.0]: https://github.com/diesel-rs/diesel/compare/v0.8.2...v0.9.0 +[0.9.1]: https://github.com/diesel-rs/diesel/compare/v0.9.0...v0.9.1 +[0.10.0]: https://github.com/diesel-rs/diesel/compare/v0.9.1...v0.10.0 +[0.10.1]: https://github.com/diesel-rs/diesel/compare/v0.10.0...v0.10.1 +[0.11.0]: https://github.com/diesel-rs/diesel/compare/v0.10.1...v0.11.0 +[0.11.1]: https://github.com/diesel-rs/diesel/compare/v0.11.0...v0.11.1 +[0.11.2]: https://github.com/diesel-rs/diesel/compare/v0.11.1...v0.11.2 +[0.11.4]: https://github.com/diesel-rs/diesel/compare/v0.11.2...v0.11.4 +[0.12.0]: https://github.com/diesel-rs/diesel/compare/v0.11.4...v0.12.0 +[0.12.1]: https://github.com/diesel-rs/diesel/compare/v0.12.0...v0.12.1 +[0.13.0]: https://github.com/diesel-rs/diesel/compare/v0.12.1...v0.13.0 +[0.14.0]: https://github.com/diesel-rs/diesel/compare/v0.13.0...v0.14.0 +[0.14.1]: https://github.com/diesel-rs/diesel/compare/v0.14.0...v0.14.1 +[0.15.0]: https://github.com/diesel-rs/diesel/compare/v0.14.1...v0.15.0 +[0.15.1]: https://github.com/diesel-rs/diesel/compare/v0.15.0...v0.15.1 +[0.15.2]: https://github.com/diesel-rs/diesel/compare/v0.15.1...v0.15.2 +[0.16.0]: https://github.com/diesel-rs/diesel/compare/v0.15.2...v0.16.0 +[0.99.0]: https://github.com/diesel-rs/diesel/compare/v0.16.0...v0.99.0 +[0.99.1]: https://github.com/diesel-rs/diesel/compare/v0.99.0...v0.99.1 +[1.0.0]: https://github.com/diesel-rs/diesel/compare/v0.99.1...v1.0.0 +[1.1.0]: https://github.com/diesel-rs/diesel/compare/v1.0.0...v1.1.0 +[1.1.1]: https://github.com/diesel-rs/diesel/compare/v1.1.0...v1.1.1 +[1.1.2]: https://github.com/diesel-rs/diesel/compare/v1.1.1...v1.1.2 +[1.2.0]: https://github.com/diesel-rs/diesel/compare/v1.1.2...v1.2.0 +[1.2.1]: https://github.com/diesel-rs/diesel/compare/v1.2.0...v1.2.1 +[1.2.2]: https://github.com/diesel-rs/diesel/compare/v1.2.1...v1.2.2 +[1.3.0]: https://github.com/diesel-rs/diesel/compare/v1.2.2...v1.3.0 +[1.3.1]: https://github.com/diesel-rs/diesel/compare/v1.3.0...v1.3.1 +[1.3.2]: https://github.com/diesel-rs/diesel/compare/v1.3.1...v1.3.2 +[1.3.3]: https://github.com/diesel-rs/diesel/compare/v1.3.2...v1.3.3 +[1.4.0]: https://github.com/diesel-rs/diesel/compare/v1.3.0...v1.4.0 +[1.4.1]: https://github.com/diesel-rs/diesel/compare/v1.4.0...v1.4.1 +[1.4.2]: https://github.com/diesel-rs/diesel/compare/v1.4.1...v1.4.2 +[1.4.3]: https://github.com/diesel-rs/diesel/compare/v1.4.2...v1.4.3 +[1.4.4]: https://github.com/diesel-rs/diesel/compare/v1.4.3...v1.4.4 diff --git a/collector/benchmarks/diesel/CONTRIBUTING.md b/collector/benchmarks/diesel/CONTRIBUTING.md new file mode 100644 index 000000000..c0d35c2e8 --- /dev/null +++ b/collector/benchmarks/diesel/CONTRIBUTING.md @@ -0,0 +1,177 @@ +# Contributing + +Thanks for your interest in contributing to Diesel! We very much look forward to +your suggestions, bug reports, and pull requests. + +We run an active [Gitter +channel](https://gitter.im/diesel-rs/diesel) where you can ask Diesel-related questions and +get help. Feel free to ask there before opening a GitHub issue or +pull request. + +*Note:* Anyone who interacts with Diesel in any space, including but not +limited to this GitHub repository, must follow our [code of +conduct](https://github.com/diesel-rs/diesel/blob/master/code_of_conduct.md). + + +## Submitting bug reports + +Have a look at our [issue tracker]. If you can't find an issue (open or closed) +describing your problem (or a very similar one) there, please open a new issue with +the following details: + +- Which versions of Rust and Diesel are you using? +- Which feature flags are you using? +- What are you trying to accomplish? +- What is the full error you are seeing? +- How can we reproduce this? + - Please quote as much of your code as needed to reproduce (best link to a + public repository or [Gist]) + - Please post as much of your database schema as is relevant to your error + +[issue tracker]: https://github.com/diesel-rs/diesel/issues +[Gist]: https://gist.github.com + +Thank you! We'll try to respond as quickly as possible. + + +## Submitting feature requests + +If you can't find an issue (open or closed) describing your idea on our [issue +tracker], open an issue. Adding answers to the following +questions in your description is +1: + +- What do you want to do, and how do you expect Diesel to support you with that? +- How might this be added to Diesel? +- What are possible alternatives? +- Are there any disadvantages? + +Thank you! We'll try to respond as quickly as possible. + + +## Contribute code to Diesel + +### Setting up Diesel locally + +1. Install Rust using [rustup], which allows you to easily switch between Rust + versions. Diesel currently supports Rust Stable, Nightly, Rust Beta. + +2. Install the system libraries needed to interface with the database systems + you wish to use. + + These are the same as when compiling Diesel. It's generally a good idea + to install _all_ drivers so you can run all tests locally. + + *Shortcut:* On macOS, you don't need to install anything to work with SQLite. + For PostgreSQL, you'll only need the server (`libpq` is installed by + default). To get started, `brew install postgresql mysql` and follow the + instructions shown to set up the database servers. +3. Clone this repository and open it in your favorite editor. +4. Create a `.env` file in this directory, and add the connection details for + your databases. + + *Additional note:* The MySQL tests currently fail when running on MySQL 5.6 + or lower. If you have 5.6 or lower installed locally and cannot upgrade for + some reason, you may want to consider setting up Docker as mentioned below. + + See [.env.sample](.env.sample) for an example that works with a trivial + local setup. + + *Note:* If you didn't specify the MySQL user to be one with elevated + permissions, you'll want to run a command like ```mysql -c "GRANT ALL ON + `diesel_%`.* TO ''@'localhost';" -uroot```, or something similar for the + user that you've specified. + + If you have [Docker](https://www.docker.com/), the following snippet might help you + to get Postgres and MySQL running (with the above `.env` file): + + ```bash + #!/usr/bin/env sh + set -e + docker run -d --name diesel.mysql -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql + while + sleep 1; + ! echo 'CREATE DATABASE diesel_test; CREATE DATABASE diesel_unit_test;' | docker exec -i diesel.mysql mysql + do sleep 1; done + + docker run -d --name diesel.postgres -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres + while + sleep 1; + ! echo 'CREATE DATABASE diesel_test;' | docker exec -i diesel.postgres psql -U postgres + do :; done + ``` + + If you want to use docker-compose, you can execute docker-compose command like this. + + ```bash + $ docker-compose up + ``` + +5. Now, try running the test suite to confirm everything works for you locally + by executing `bin/test`. (Initially, this will take a while to compile + everything.) + +[rustup]: https://rustup.rs/ + +### Coding Style + +We follow the [Rust Style Guide](https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/guide.md), enforced using [rustfmt](https://github.com/rust-lang/rustfmt). +To run rustfmt tests locally: + +1. Use rustup to set rust toolchain to the version specified in the + [rust-toolchain file](./rust-toolchain). + +2. Install the rustfmt and clippy by running + ``` + rustup component add rustfmt-preview + rustup component add clippy-preview + ``` + +3. Run clippy using cargo from the root of your diesel repo. + ``` + cargo clippy + ``` + Each PR needs to compile without warning. + +4. Run rustfmt using cargo from the root of your diesel repo. + + To see changes that need to be made, run + + ``` + cargo fmt --all -- --check + ``` + + If all code is properly formatted (e.g. if you have not made any changes), + this should run without error or output. + If your code needs to be reformatted, + you will see a diff between your code and properly formatted code. + If you see code here that you didn't make any changes to + then you are probably running the wrong version of rustfmt. + Once you are ready to apply the formatting changes, run + + ``` + cargo fmt --all + ``` + + You won't see any output, but all your files will be corrected. + +You can also use rustfmt to make corrections or highlight issues in your editor. +Check out [their README](https://github.com/rust-lang/rustfmt) for details. + + +### Common Abbreviations + +`ST`: Sql Type. Basically always has the `NativeSqlType` constraint + +`DB`: Database. Basically always has the `Backend` constraint. + +`QS`: Query Source. Usually doesn't have a constraint, but sometimes will have `QuerySource` attached + +`PK`: Primary Key + +`Lhs`: Left Hand Side + +`Rhs`: Right Hand Side + +`Conn`: Connection + +Generally, we prefer to give our types meaningful names. `Lhs` and `Rhs` vs `T` and `U` for a binary expression, for example. diff --git a/collector/benchmarks/diesel/Cargo.lock b/collector/benchmarks/diesel/Cargo.lock new file mode 100644 index 000000000..5c53405c4 --- /dev/null +++ b/collector/benchmarks/diesel/Cargo.lock @@ -0,0 +1,590 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bigdecimal" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc403c26e6b03005522e6e8053384c4e881dfe5b2bf041c0c2c49be33d64a539" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "winapi", +] + +[[package]] +name = "cloudabi" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" +dependencies = [ + "bitflags", +] + +[[package]] +name = "diesel" +version = "2.0.0" +dependencies = [ + "bigdecimal", + "bitflags", + "byteorder", + "cfg-if 0.1.10", + "chrono", + "diesel_derives", + "dotenv", + "ipnetwork", + "itoa", + "libc", + "libsqlite3-sys", + "mysqlclient-sys", + "num-bigint", + "num-integer", + "num-traits", + "percent-encoding", + "pq-sys", + "quickcheck", + "r2d2", + "serde_json", + "url", + "uuid", +] + +[[package]] +name = "diesel_derives" +version = "2.0.0" +dependencies = [ + "cfg-if 0.1.10", + "diesel", + "dotenv", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "form_urlencoded" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "getrandom" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "wasi", +] + +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "instant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "ipnetwork" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c3eaab3ac0ede60ffa41add21970a7df7d91772c03383aac6c2c3d53cc716b" +dependencies = [ + "serde", +] + +[[package]] +name = "itoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" + +[[package]] +name = "libsqlite3-sys" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d31059f22935e6c31830db5249ba2b7ecd54fd73a9909286f0a67aa55c2fbd" +dependencies = [ + "pkg-config", + "vcpkg", +] + +[[package]] +name = "lock_api" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +dependencies = [ + "cfg-if 0.1.10", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "mysqlclient-sys" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9637d93448044078aaafea7419aed69d301b4a12bcc4aa0ae856eb169bef85" +dependencies = [ + "pkg-config", + "vcpkg", +] + +[[package]] +name = "num-bigint" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e9a41747ae4633fce5adffb4d2e81ffc5e89593cb19917f8fb2cc5ff76507bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" +dependencies = [ + "cfg-if 0.1.10", + "cloudabi", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "pq-sys" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac25eee5a0582f45a67e837e350d784e7003bd29a5f460796772061ca49ffda" +dependencies = [ + "vcpkg", +] + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quickcheck" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" +dependencies = [ + "env_logger", + "log", + "rand", + "rand_core", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r2d2" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f" +dependencies = [ + "log", + "parking_lot", + "scheduled-thread-pool", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "regex" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "scheduled-thread-pool" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" + +[[package]] +name = "serde_json" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "smallvec" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acad6f34eb9e8a259d3283d1e8c1d34d7415943d4895f65cc73813c7396fc85" + +[[package]] +name = "syn" +version = "1.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8833e20724c24de12bbaba5ad230ea61c3eafb05b881c7c9d3cfe8638b187e68" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tinyvec" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "url" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "uuid" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" + +[[package]] +name = "vcpkg" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/collector/benchmarks/diesel/Cargo.toml b/collector/benchmarks/diesel/Cargo.toml new file mode 100644 index 000000000..a52e2d521 --- /dev/null +++ b/collector/benchmarks/diesel/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ + "diesel", + "diesel_derives" +] diff --git a/collector/benchmarks/diesel/LICENSE-APACHE b/collector/benchmarks/diesel/LICENSE-APACHE new file mode 100644 index 000000000..164de5d86 --- /dev/null +++ b/collector/benchmarks/diesel/LICENSE-APACHE @@ -0,0 +1,190 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2015-2018 Sean Griffin + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/collector/benchmarks/diesel/LICENSE-MIT b/collector/benchmarks/diesel/LICENSE-MIT new file mode 100644 index 000000000..4e932e5cc --- /dev/null +++ b/collector/benchmarks/diesel/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2018 Sean Griffin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/collector/benchmarks/diesel/README.md b/collector/benchmarks/diesel/README.md new file mode 100644 index 000000000..76f53668e --- /dev/null +++ b/collector/benchmarks/diesel/README.md @@ -0,0 +1,62 @@ +[![](https://diesel.rs/assets/images/diesel_logo_stacked_black.png)](https://diesel.rs) + +A safe, extensible ORM and Query Builder for Rust +========================================================== +[![Build Status](https://github.com/diesel-rs/diesel/workflows/CI%20Tests/badge.svg)](https://github.com/diesel-rs/diesel/actions?query=workflow%3A%22CI+Tests%22+branch%3Amaster) +[![Gitter](https://badges.gitter.im/diesel-rs/diesel.svg)](https://gitter.im/diesel-rs/diesel?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +[![Crates.io](https://img.shields.io/crates/v/diesel.svg)](https://crates.io/crates/diesel) + +API Documentation: [latest release](https://docs.rs/diesel) – [master branch](https://docs.diesel.rs/master/diesel/index.html) + +[Homepage](https://diesel.rs) + +Diesel gets rid of the boilerplate for database interaction and eliminates +runtime errors without sacrificing performance. It takes full advantage of +Rust's type system to create a low overhead query builder that "feels like +Rust." + +Supported databases: +1. [PostgreSQL](https://docs.diesel.rs/diesel/pg/index.html) +2. [MySQL](https://docs.diesel.rs/diesel/mysql/index.html) +3. [SQLite](https://docs.diesel.rs/diesel/sqlite/index.html) + +You can configure the database backend in `Cargo.toml`: +```toml +[dependencies] +diesel = { version = "", features = [""] } +``` + +## Getting Started + +Find our extensive Getting Started tutorial at +[https://diesel.rs/guides/getting-started](https://diesel.rs/guides/getting-started). +Guides on more specific features are coming soon. + +## Getting help +If you run into problems, Diesel has a very active Gitter room. +You can come ask for help at +[gitter.im/diesel-rs/diesel](https://gitter.im/diesel-rs/diesel). +For help with longer questions and discussion about the future of Diesel, +visit our [discourse](https://discourse.diesel.rs/) forum. + +## Code of conduct + +Anyone who interacts with Diesel in any space, including but not limited to +this GitHub repository, must follow our [code of conduct](https://github.com/diesel-rs/diesel/blob/master/code_of_conduct.md). + +## License + +Licensed under either of these: + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + https://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or + https://opensource.org/licenses/MIT) + +### Contributing +Before contributing, please read the [contributors guide](https://github.com/diesel-rs/diesel/blob/master/CONTRIBUTING.md) +for useful information about setting up Diesel locally, coding style and common abbreviations. + +Unless you explicitly state otherwise, any contribution you intentionally submit +for inclusion in the work, as defined in the Apache-2.0 license, shall be +dual-licensed as above, without any additional terms or conditions. diff --git a/collector/benchmarks/diesel/clippy.toml b/collector/benchmarks/diesel/clippy.toml new file mode 100644 index 000000000..ab165574d --- /dev/null +++ b/collector/benchmarks/diesel/clippy.toml @@ -0,0 +1,10 @@ +cognitive-complexity-threshold = 30 +doc-valid-idents = [ + "MiB", "GiB", "TiB", "PiB", "EiB", + "DirectX", "OpenGL", "TrueType", + "GPLv2", "GPLv3", + "GitHub", + "IPv4", "IPv6", + "JavaScript", "NaN", "OAuth", + "SQLite", "PostgreSQL", "MySQL" +] diff --git a/collector/benchmarks/diesel/code_of_conduct.md b/collector/benchmarks/diesel/code_of_conduct.md new file mode 100644 index 000000000..0374fa064 --- /dev/null +++ b/collector/benchmarks/diesel/code_of_conduct.md @@ -0,0 +1,80 @@ +# Contributor Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting a project maintainer at: + +* Sean Griffin +* Pascal Hertleif +* Bastien Orivel + +All complaints will be reviewed and investigated and will result in a response +that is deemed necessary and appropriate to the circumstances. The project team +is obligated to maintain confidentiality with regard to the reporter of an +incident. Further details of specific enforcement policies may be posted +separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [https://contributor-covenant.org/version/1/4][version] + +[homepage]: https://contributor-covenant.org +[version]: https://contributor-covenant.org/version/1/4/ diff --git a/collector/benchmarks/diesel/diesel/Cargo.toml b/collector/benchmarks/diesel/diesel/Cargo.toml new file mode 100644 index 000000000..c2a75950b --- /dev/null +++ b/collector/benchmarks/diesel/diesel/Cargo.toml @@ -0,0 +1,65 @@ +[package] +name = "diesel" +version = "2.0.0" +authors = ["Sean Griffin "] +license = "MIT OR Apache-2.0" +description = "A safe, extensible ORM and Query Builder for PostgreSQL, SQLite, and MySQL" +readme = "README.md" +documentation = "https://docs.rs/diesel/" +homepage = "https://diesel.rs" +repository = "https://github.com/diesel-rs/diesel" +keywords = ["orm", "database", "blockchain", "sql"] +categories = ["database"] +edition = "2018" + +[dependencies] +byteorder = "1.0" +chrono = { version = "0.4.19", optional = true, default-features = false, features = ["clock", "std"] } +libc = { version = "0.2.0", optional = true } +libsqlite3-sys = { version = ">=0.8.0, <0.21.0", optional = true, features = ["min_sqlite_version_3_7_16"] } +mysqlclient-sys = { version = "0.2.0", optional = true } +pq-sys = { version = "0.4.0", optional = true } +quickcheck = { version = "0.9.0", optional = true } +serde_json = { version = ">=0.8.0, <2.0", optional = true } +url = { version = "2.1.0", optional = true } +percent-encoding = { version = "2.1.0", optional = true } +uuid = { version = ">=0.7.0, <0.9.0", optional = true} +ipnetwork = { version = ">=0.12.2, <0.18.0", optional = true } +num-bigint = { version = ">=0.2.0, <0.4.0", optional = true } +num-traits = { version = "0.2.0", optional = true } +num-integer = { version = "0.1.39", optional = true } +bigdecimal = { version = ">=0.0.13, < 0.3.0", optional = true } +bitflags = { version = "1.2.0", optional = true } +r2d2 = { version = ">= 0.8.0, < 0.9.0", optional = true } +itoa = "0.4.0" + +[dependencies.diesel_derives] +version = "~2.0.0" +path = "../diesel_derives" + +[dev-dependencies] +cfg-if = "0.1.10" +dotenv = "0.15" +ipnetwork = ">=0.12.2, <0.18.0" +quickcheck = "0.9" + +[features] +default = ["128-column-tables"] +extras = ["chrono", "serde_json", "uuid", "network-address", "numeric", "r2d2"] +unstable = ["diesel_derives/nightly"] +large-tables = ["32-column-tables"] +huge-tables = ["64-column-tables"] +32-column-tables = [] +64-column-tables = ["32-column-tables"] +128-column-tables = ["64-column-tables"] +postgres = ["pq-sys", "bitflags", "diesel_derives/postgres"] +sqlite = ["libsqlite3-sys", "diesel_derives/sqlite"] +mysql = ["mysqlclient-sys", "url", "percent-encoding", "diesel_derives/mysql", "bitflags"] +without-deprecated = [] +with-deprecated = [] +network-address = ["ipnetwork", "libc"] +numeric = ["num-bigint", "bigdecimal", "num-traits", "num-integer"] + +[package.metadata.docs.rs] +features = ["postgres", "mysql", "sqlite", "extras"] +no-default-features = true diff --git a/collector/benchmarks/diesel/diesel/LICENSE-APACHE b/collector/benchmarks/diesel/diesel/LICENSE-APACHE new file mode 120000 index 000000000..965b606f3 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/collector/benchmarks/diesel/diesel/LICENSE-MIT b/collector/benchmarks/diesel/diesel/LICENSE-MIT new file mode 120000 index 000000000..76219eb72 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/collector/benchmarks/diesel/diesel/README.md b/collector/benchmarks/diesel/diesel/README.md new file mode 100644 index 000000000..0e150e8d3 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/README.md @@ -0,0 +1,9 @@ +[![Diesel](https://diesel.rs/assets/images/diesel_logo_stacked_black.png)](https://diesel.rs) + +# Diesel - A safe, extensible ORM and Query Builder for Rust + +Diesel is the most productive way to interact with databases in Rust because of its safe and composable abstractions over queries. + +## Getting Started + +This is the Readme of the main crate. You can find [an extensive Getting Started tutorial](https://diesel.rs/guides/getting-started) and more information on our [website](https://diesel.rs). diff --git a/collector/benchmarks/diesel/diesel/src/associations/belongs_to.rs b/collector/benchmarks/diesel/diesel/src/associations/belongs_to.rs new file mode 100644 index 000000000..377c01ad0 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/associations/belongs_to.rs @@ -0,0 +1,178 @@ +use super::{HasTable, Identifiable}; +use crate::dsl::{Eq, EqAny, Filter, FindBy}; +use crate::expression::array_comparison::AsInExpression; +use crate::expression::AsExpression; +use crate::prelude::*; +use crate::query_dsl::methods::FilterDsl; +use crate::sql_types::SqlType; + +use std::borrow::Borrow; +use std::hash::Hash; + +/// Indicates that a type belongs to `Parent` +/// +/// Specifically, this means that this struct has fields +/// which correspond to the primary key of `Parent`. +/// This implies that a foreign key relationship exists on the tables. +/// +/// This trait is not capable of supporting composite foreign keys +pub trait BelongsTo { + /// The foreign key of this struct + type ForeignKey: Hash + ::std::cmp::Eq; + /// The database column representing the foreign key + /// of the table this struct represents + type ForeignKeyColumn: Column; + + /// Returns the foreign key for `self` + fn foreign_key(&self) -> Option<&Self::ForeignKey>; + /// Returns the foreign key column of this struct's table + fn foreign_key_column() -> Self::ForeignKeyColumn; +} + +/// The `grouped_by` function groups records by their parent. +/// +/// `grouped_by` is called on a `Vec` with a `&[Parent]`. +/// The return value will be `Vec>` indexed to match their parent. +/// Or to put it another way, the returned data can be passed to `zip`, +/// and it will be combined with its parent. +/// This function does not generate a `GROUP BY` SQL statement, +/// as it operates on data structures already loaded from the database +/// +/// **Child** refers to the "many" part of a "one to many" relationship. It "belongs to" its parent +/// **Parent** refers to the "one" part of a "one to many" relationship and can "have many" children. +/// The child always has a foreign key, which refers to its parent's primary key. +/// In the following relationship, User has many Posts, +/// so User is the parent and Posts are children. +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use schema::{posts, users}; +/// # +/// # #[derive(Identifiable, Queryable, PartialEq, Debug)] +/// # pub struct User { +/// # id: i32, +/// # name: String, +/// # } +/// # +/// # #[derive(Debug, PartialEq)] +/// # #[derive(Identifiable, Queryable, Associations)] +/// # #[belongs_to(User)] +/// # pub struct Post { +/// # id: i32, +/// # user_id: i32, +/// # title: String, +/// # } +/// # +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let connection = establish_connection(); +/// let users = users::table.load::(&connection)?; +/// let posts = Post::belonging_to(&users) +/// .load::(&connection)? +/// .grouped_by(&users); +/// let data = users.into_iter().zip(posts).collect::>(); +/// +/// let expected_data = vec![ +/// ( +/// User { id: 1, name: "Sean".into() }, +/// vec![ +/// Post { id: 1, user_id: 1, title: "My first post".into() }, +/// Post { id: 2, user_id: 1, title: "About Rust".into() }, +/// ], +/// ), +/// ( +/// User { id: 2, name: "Tess".into() }, +/// vec![ +/// Post { id: 3, user_id: 2, title: "My first post too".into() }, +/// ], +/// ), +/// ]; +/// +/// assert_eq!(expected_data, data); +/// # Ok(()) +/// # } +/// ``` +/// +/// See [the module documentation] for more examples +/// +/// [the module documentation]: index.html +pub trait GroupedBy<'a, Parent>: IntoIterator + Sized { + /// See the trait documentation. + fn grouped_by(self, parents: &'a [Parent]) -> Vec>; +} + +type Id = ::Id; + +impl<'a, Parent: 'a, Child, Iter> GroupedBy<'a, Parent> for Iter +where + Iter: IntoIterator, + Child: BelongsTo, + &'a Parent: Identifiable, + Id<&'a Parent>: Borrow, +{ + fn grouped_by(self, parents: &'a [Parent]) -> Vec> { + use std::collections::HashMap; + + let id_indices: HashMap<_, _> = parents + .iter() + .enumerate() + .map(|(i, u)| (u.id(), i)) + .collect(); + let mut result = parents.iter().map(|_| Vec::new()).collect::>(); + for child in self { + if let Some(index) = child.foreign_key().map(|i| id_indices[i]) { + result[index].push(child); + } + } + result + } +} + +impl<'a, Parent, Child> BelongingToDsl<&'a Parent> for Child +where + &'a Parent: Identifiable, + Child: HasTable + BelongsTo, + Id<&'a Parent>: AsExpression<::SqlType>, + Child::Table: FilterDsl>>, + Child::ForeignKeyColumn: ExpressionMethods, + ::SqlType: SqlType, +{ + type Output = FindBy>; + + fn belonging_to(parent: &'a Parent) -> Self::Output { + FilterDsl::filter(Child::table(), Child::foreign_key_column().eq(parent.id())) + } +} + +impl<'a, Parent, Child> BelongingToDsl<&'a [Parent]> for Child +where + &'a Parent: Identifiable, + Child: HasTable + BelongsTo, + Vec>: AsInExpression<::SqlType>, + ::Table: FilterDsl>>>, + Child::ForeignKeyColumn: ExpressionMethods, + ::SqlType: SqlType, +{ + type Output = Filter>>>; + + fn belonging_to(parents: &'a [Parent]) -> Self::Output { + let ids = parents.iter().map(Identifiable::id).collect::>(); + FilterDsl::filter(Child::table(), Child::foreign_key_column().eq_any(ids)) + } +} + +impl<'a, Parent, Child> BelongingToDsl<&'a Vec> for Child +where + Child: BelongingToDsl<&'a [Parent]>, +{ + type Output = Child::Output; + + fn belonging_to(parents: &'a Vec) -> Self::Output { + Self::belonging_to(&**parents) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/associations/mod.rs b/collector/benchmarks/diesel/diesel/src/associations/mod.rs new file mode 100644 index 000000000..c04fe0442 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/associations/mod.rs @@ -0,0 +1,413 @@ +//! Traits related to relationships between multiple tables. +//! +//! Associations in Diesel are always child-to-parent. +//! You can declare an association between two records with `#[belongs_to]`. +//! Unlike other ORMs, Diesel has no concept of `has many` +//! +//! ```rust +//! # include!("../doctest_setup.rs"); +//! use schema::{posts, users}; +//! +//! #[derive(Identifiable, Queryable, PartialEq, Debug)] +//! #[table_name = "users"] +//! pub struct User { +//! id: i32, +//! name: String, +//! } +//! +//! #[derive(Identifiable, Queryable, Associations, PartialEq, Debug)] +//! #[belongs_to(User)] +//! #[table_name = "posts"] +//! pub struct Post { +//! id: i32, +//! user_id: i32, +//! title: String, +//! } +//! +//! # fn main() { +//! # run_test().unwrap(); +//! # } +//! # +//! # fn run_test() -> QueryResult<()> { +//! # let connection = establish_connection(); +//! # use self::users::dsl::*; +//! let user = users.find(2).get_result::(&connection)?; +//! let users_post = Post::belonging_to(&user) +//! .first(&connection)?; +//! let expected = Post { id: 3, user_id: 2, title: "My first post too".into() }; +//! assert_eq!(expected, users_post); +//! # Ok(()) +//! # } +//! ``` +//! +//! Note that in addition to the `#[belongs_to]` annotation, we also need to +//! `#[derive(Associations)]` +//! +//! `#[belongs_to]` is given the name of the struct that represents the parent. +//! Both the parent and child must implement [`Identifiable`]. +//! The struct given to `#[belongs_to]` must be in scope, +//! so you will need `use some_module::User` if `User` is defined in another module. +//! +//! If the parent record is generic over lifetimes, they can be written as `'_`. +//! You will also need to wrap the type in quotes until +//! `unrestricted_attribute_tokens` is stable. +//! +//! ```rust +//! # include!("../doctest_setup.rs"); +//! # use schema::{posts, users}; +//! # use std::borrow::Cow; +//! # +//! #[derive(Identifiable)] +//! #[table_name = "users"] +//! pub struct User<'a> { +//! id: i32, +//! name: Cow<'a, str>, +//! } +//! +//! #[derive(Associations)] +//! #[belongs_to(parent = "User<'_>")] +//! #[table_name = "posts"] +//! pub struct Post { +//! id: i32, +//! user_id: i32, +//! title: String, +//! } +//! # +//! # fn main() {} +//! ``` +//! +//! [`Identifiable`]: trait.Identifiable.html +//! +//! By default, Diesel assumes that your foreign keys will follow the convention `table_name_id`. +//! If your foreign key has a different name, +//! you can provide the `foreign_key` argument to `#[belongs_to]`. +//! For example, `#[belongs_to(Foo, foreign_key = "mykey")]`. +//! +//! Associated data is typically loaded in multiple queries (one query per table). +//! This is usually more efficient than using a join, +//! especially if 3 or more tables are involved. +//! For most datasets, +//! using a join to load in a single query transmits so much duplicate data +//! that it costs more time than the extra round trip would have. +//! +//! You can load the children for one or more parents using +//! [`belonging_to`] +//! +//! [`belonging_to`]: ../query_dsl/trait.BelongingToDsl.html#tymethod.belonging_to +//! +//! ```rust +//! # include!("../doctest_setup.rs"); +//! # use schema::users; +//! # use schema::posts; +//! # +//! # #[derive(Debug, PartialEq, Identifiable, Queryable)] +//! # pub struct User { +//! # id: i32, +//! # name: String, +//! # } +//! # +//! # #[derive(Debug, PartialEq, Identifiable, Queryable, Associations)] +//! # #[belongs_to(User)] +//! # pub struct Post { +//! # id: i32, +//! # user_id: i32, +//! # title: String, +//! # } +//! # +//! # fn main() { +//! # use self::users::dsl::*; +//! # let connection = establish_connection(); +//! # +//! let user = users.find(1).first::(&connection).expect("Error loading user"); +//! let post_list = Post::belonging_to(&user) +//! .load::(&connection) +//! .expect("Error loading posts"); +//! let expected = vec![ +//! Post { id: 1, user_id: 1, title: "My first post".to_string() }, +//! Post { id: 2, user_id: 1, title: "About Rust".to_string() }, +//! ]; +//! +//! assert_eq!(post_list, expected); +//! # } +//! ``` +//! +//! If you're coming from other ORMs, you'll notice that this design is quite different from most. +//! There you would have an instance method on the parent, or have the children stored somewhere on +//! the posts. This design leads to many problems, including [N+1 query +//! bugs][load-your-entire-database-into-memory-lol], and runtime errors when accessing an +//! association that isn't there. +//! +//! [load-your-entire-database-into-memory-lol]: https://stackoverflow.com/q/97197/1254484 +//! +//! In Diesel, data and its associations are considered to be separate. If you want to pass around +//! a user and all of its posts, that type is `(User, Vec)`. +//! +//! Next lets look at how to load the children for more than one parent record. +//! [`belonging_to`] can be used to load the data, but we'll also need to group it +//! with its parents. For this we use an additional method [`grouped_by`]. +//! +//! [`grouped_by`]: trait.GroupedBy.html#tymethod.grouped_by +//! [`belonging_to`]: ../query_dsl/trait.BelongingToDsl.html#tymethod.belonging_to +//! +//! ```rust +//! # include!("../doctest_setup.rs"); +//! # use schema::{posts, users}; +//! # +//! # #[derive(Identifiable, Queryable)] +//! # pub struct User { +//! # id: i32, +//! # name: String, +//! # } +//! # +//! # #[derive(Debug, PartialEq)] +//! # #[derive(Identifiable, Queryable, Associations)] +//! # #[belongs_to(User)] +//! # pub struct Post { +//! # id: i32, +//! # user_id: i32, +//! # title: String, +//! # } +//! # +//! # fn main() { +//! # run_test(); +//! # } +//! # +//! # fn run_test() -> QueryResult<()> { +//! # let connection = establish_connection(); +//! # use self::users::dsl::*; +//! # use self::posts::dsl::{posts, title}; +//! let sean = users.filter(name.eq("Sean")).first::(&connection)?; +//! let tess = users.filter(name.eq("Tess")).first::(&connection)?; +//! +//! let seans_posts = Post::belonging_to(&sean) +//! .select(title) +//! .load::(&connection)?; +//! assert_eq!(vec!["My first post", "About Rust"], seans_posts); +//! +//! // A vec or slice can be passed as well +//! let more_posts = Post::belonging_to(&vec![sean, tess]) +//! .select(title) +//! .load::(&connection)?; +//! assert_eq!(vec!["My first post", "About Rust", "My first post too"], more_posts); +//! # Ok(()) +//! # } +//! ``` +//! +//! Typically you will want to group up the children with their parents. +//! In other ORMs, this is often called a `has_many` relationship. +//! Diesel provides support for doing this grouping, once the data has been +//! loaded. +//! +//! [`grouped_by`] is called on a `Vec` with a `&[Parent]`. +//! The return value will be `Vec>` indexed to match their parent. +//! Or to put it another way, the returned data can be passed to `zip`, +//! and it will be combined with its parent. +//! +//! ```rust +//! # include!("../doctest_setup.rs"); +//! # use schema::{posts, users}; +//! # +//! # #[derive(Identifiable, Queryable, PartialEq, Debug)] +//! # pub struct User { +//! # id: i32, +//! # name: String, +//! # } +//! # +//! # #[derive(Debug, PartialEq)] +//! # #[derive(Identifiable, Queryable, Associations)] +//! # #[belongs_to(User)] +//! # pub struct Post { +//! # id: i32, +//! # user_id: i32, +//! # title: String, +//! # } +//! # +//! # fn main() { +//! # run_test(); +//! # } +//! # +//! # fn run_test() -> QueryResult<()> { +//! # let connection = establish_connection(); +//! let users = users::table.load::(&connection)?; +//! let posts = Post::belonging_to(&users) +//! .load::(&connection)? +//! .grouped_by(&users); +//! let data = users.into_iter().zip(posts).collect::>(); +//! +//! let expected_data = vec![ +//! ( +//! User { id: 1, name: "Sean".into() }, +//! vec![ +//! Post { id: 1, user_id: 1, title: "My first post".into() }, +//! Post { id: 2, user_id: 1, title: "About Rust".into() }, +//! ], +//! ), +//! ( +//! User { id: 2, name: "Tess".into() }, +//! vec![ +//! Post { id: 3, user_id: 2, title: "My first post too".into() }, +//! ], +//! ), +//! ]; +//! +//! assert_eq!(expected_data, data); +//! # Ok(()) +//! # } +//! ``` +//! +//! [`grouped_by`] can be called multiple times +//! if you have multiple children or grandchildren. +//! +//! For example, this code will load some users, +//! all of their posts, +//! and all of the comments on those posts. +//! Explicit type annotations have been added +//! to make each line a bit more clear. +//! +//! ```rust +//! # include!("../doctest_setup.rs"); +//! # use schema::{users, posts, comments}; +//! # +//! # #[derive(Debug, PartialEq, Identifiable, Queryable)] +//! # pub struct User { +//! # id: i32, +//! # name: String, +//! # } +//! # +//! # #[derive(Debug, PartialEq, Identifiable, Queryable, Associations)] +//! # #[belongs_to(User)] +//! # pub struct Post { +//! # id: i32, +//! # user_id: i32, +//! # title: String, +//! # } +//! # +//! # #[derive(Debug, PartialEq, Identifiable, Queryable, Associations)] +//! # #[belongs_to(Post)] +//! # pub struct Comment { +//! # id: i32, +//! # post_id: i32, +//! # body: String, +//! # } +//! # +//! # fn main() { +//! # let connection = establish_connection(); +//! # +//! let users: Vec = users::table.load::(&connection) +//! .expect("error loading users"); +//! let posts: Vec = Post::belonging_to(&users) +//! .load::(&connection) +//! .expect("error loading posts"); +//! let comments: Vec = Comment::belonging_to(&posts) +//! .load::(&connection) +//! .expect("Error loading comments"); +//! let grouped_comments: Vec> = comments.grouped_by(&posts); +//! let posts_and_comments: Vec)>> = posts +//! .into_iter() +//! .zip(grouped_comments) +//! .grouped_by(&users); +//! let result: Vec<(User, Vec<(Post, Vec)>)> = users +//! .into_iter() +//! .zip(posts_and_comments) +//! .collect(); +//! let expected = vec![ +//! ( +//! User { id: 1, name: "Sean".to_string() }, +//! vec![ +//! ( +//! Post { id: 1, user_id: 1, title: "My first post".to_string() }, +//! vec![ Comment { id: 1, post_id: 1, body: "Great post".to_string() } ] +//! ), +//! ( +//! Post { id: 2, user_id: 1, title: "About Rust".to_string() }, +//! vec![ +//! Comment { id: 2, post_id: 2, body: "Yay! I am learning Rust".to_string() } +//! ] +//! +//! ) +//! ] +//! ), +//! ( +//! User { id: 2, name: "Tess".to_string() }, +//! vec![ +//! ( +//! Post { id: 3, user_id: 2, title: "My first post too".to_string() }, +//! vec![ Comment { id: 3, post_id: 3, body: "I enjoyed your post".to_string() } ] +//! ) +//! ] +//! ) +//! ]; +//! +//! assert_eq!(result, expected); +//! # } +//! ``` +//! +//! And that's it. +//! It may seem odd to have load, group, and zip be explicit separate steps +//! if you are coming from another ORM. +//! However, the goal is to provide simple building blocks which can +//! be used to construct the complex behavior applications need. +mod belongs_to; + +use std::hash::Hash; + +use crate::query_source::Table; + +pub use self::belongs_to::{BelongsTo, GroupedBy}; + +#[doc(inline)] +pub use diesel_derives::Associations; + +/// This trait indicates that a struct is associated with a single database table. +/// +/// This trait is implemented by structs which implement `Identifiable`, +/// as well as database tables themselves. +pub trait HasTable { + /// The table this type is associated with. + type Table: Table; + + /// Returns the table this type is associated with. + fn table() -> Self::Table; +} + +impl<'a, T: HasTable> HasTable for &'a T { + type Table = T::Table; + + fn table() -> Self::Table { + T::table() + } +} + +/// This trait indicates that a struct represents a single row in a database table. +/// +/// This must be implemented to use associations. +/// Additionally, implementing this trait allows you to pass your struct to `update` +/// (`update(&your_struct)` is equivalent to +/// `update(YourStruct::table().find(&your_struct.primary_key())`). +/// +/// This trait is usually implemented on a reference to a struct, +/// not on the struct itself. It can be [derived](derive.Identifiable.html). +/// +pub trait Identifiable: HasTable { + /// The type of this struct's identifier. + /// + /// For single-field primary keys, this is typically `&'a i32`, or `&'a String` + /// For composite primary keys, this is typically `(&'a i32, &'a i32)` + /// or `(&'a String, &'a String)`, etc. + type Id: Hash + Eq; + + /// Returns the identifier for this record. + /// + /// This takes `self` by value, not reference. + /// This is because composite primary keys + /// are typically stored as multiple fields. + /// We could not return `&(String, String)` if each string is a separate field. + /// + /// Because of Rust's rules about specifying lifetimes, + /// this means that `Identifiable` is usually implemented on references + /// so that we have a lifetime to use for `Id`. + fn id(self) -> Self::Id; +} + +#[doc(inline)] +pub use diesel_derives::Identifiable; diff --git a/collector/benchmarks/diesel/diesel/src/backend.rs b/collector/benchmarks/diesel/diesel/src/backend.rs new file mode 100644 index 000000000..d0c025c55 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/backend.rs @@ -0,0 +1,82 @@ +//! Types which represent various database backends + +use byteorder::ByteOrder; + +use crate::query_builder::bind_collector::BindCollector; +use crate::query_builder::QueryBuilder; +use crate::sql_types::{self, HasSqlType}; + +/// A database backend +/// +/// This trait represents the concept of a backend (e.g. "MySQL" vs "SQLite"). +/// It is separate from a [`Connection`](../connection/trait.Connection.html) +/// to that backend. +/// One backend may have multiple concrete connection implementations. +/// +/// Implementations of this trait should not assume details about how the +/// connection is implemented. +/// For example, the `Pg` backend does not assume that `libpq` is being used. +/// Implementations of this trait can and should care about details of the wire +/// protocol used to communicated with the database. +pub trait Backend +where + Self: Sized, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: for<'a> HasRawValue<'a>, +{ + /// The concrete `QueryBuilder` implementation for this backend. + type QueryBuilder: QueryBuilder; + /// The concrete `BindCollector` implementation for this backend. + /// + /// Most backends should use [`RawBytesBindCollector`]. + /// + /// [`RawBytesBindCollector`]: ../query_builder/bind_collector/struct.RawBytesBindCollector.html + type BindCollector: BindCollector; + /// What byte order is used to transmit integers? + /// + /// This type is only used if `RawValue` is `[u8]`. + type ByteOrder: ByteOrder; +} + +/// The raw representation of a database value given to `FromSql`. +/// +/// This trait is separate from `Backend` to imitate `type RawValue<'a>`. It +/// should only be referenced directly by implementors. Users of this type +/// should instead use the [`RawValue`](type.RawValue.html) helper type instead. +pub trait HasRawValue<'a> { + /// The actual type given to `FromSql`, with lifetimes applied. This type + /// should not be used directly. Use the [`RawValue`](type.RawValue.html) + /// helper type instead. + type RawValue; +} + +/// A trait indicating that the provided raw value uses a binary representation internally +pub trait BinaryRawValue<'a>: HasRawValue<'a> { + /// Get the underlying binary representation of the raw value + fn as_bytes(value: Self::RawValue) -> &'a [u8]; +} + +/// A helper type to get the raw representation of a database type given to +/// `FromSql`. Equivalent to `::RawValue<'a>`. +pub type RawValue<'a, DB> = >::RawValue; + +/// Does this backend support `RETURNING` clauses? +pub trait SupportsReturningClause {} +/// Does this backend support 'ON CONFLICT' clause? +pub trait SupportsOnConflictClause {} +/// Does this backend support 'WHERE' clauses on 'ON CONFLICT' clauses? +pub trait SupportsOnConflictTargetDecorations {} +/// Does this backend support the bare `DEFAULT` keyword? +pub trait SupportsDefaultKeyword {} +/// Does this backend use the standard `SAVEPOINT` syntax? +pub trait UsesAnsiSavepointSyntax {} diff --git a/collector/benchmarks/diesel/diesel/src/connection/mod.rs b/collector/benchmarks/diesel/diesel/src/connection/mod.rs new file mode 100644 index 000000000..de1cbff09 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/connection/mod.rs @@ -0,0 +1,186 @@ +//! Types related to database connections + +mod statement_cache; +mod transaction_manager; + +use std::fmt::Debug; + +use crate::backend::Backend; +use crate::deserialize::FromSqlRow; +use crate::expression::QueryMetadata; +use crate::query_builder::{AsQuery, QueryFragment, QueryId}; +use crate::result::*; + +#[doc(hidden)] +pub use self::statement_cache::{MaybeCached, StatementCache, StatementCacheKey}; +pub use self::transaction_manager::{AnsiTransactionManager, TransactionManager}; + +/// Perform simple operations on a backend. +/// +/// You should likely use [`Connection`](trait.Connection.html) instead. +pub trait SimpleConnection { + /// Execute multiple SQL statements within the same string. + /// + /// This function is used to execute migrations, + /// which may contain more than one SQL statement. + fn batch_execute(&self, query: &str) -> QueryResult<()>; +} + +/// A connection to a database +pub trait Connection: SimpleConnection + Sized + Send { + /// The backend this type connects to + type Backend: Backend; + #[doc(hidden)] + type TransactionManager: TransactionManager; + + /// Establishes a new connection to the database + /// + /// The argument to this method varies by backend. + /// See the documentation for that backend's connection class + /// for details about what it accepts. + fn establish(database_url: &str) -> ConnectionResult; + + /// Executes the given function inside of a database transaction + /// + /// If there is already an open transaction, + /// savepoints will be used instead. + /// + /// If the transaction fails to commit due to a `SerializationFailure` or a + /// `ReadOnlyTransaction` a rollback will be attempted. If the rollback succeeds, + /// the original error will be returned, otherwise the error generated by the rollback + /// will be returned. In the second case the connection should be considered broken + /// as it contains a uncommitted unabortable open transaction. + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// use diesel::result::Error; + /// + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = establish_connection(); + /// conn.transaction::<_, Error, _>(|| { + /// diesel::insert_into(users) + /// .values(name.eq("Ruby")) + /// .execute(&conn)?; + /// + /// let all_names = users.select(name).load::(&conn)?; + /// assert_eq!(vec!["Sean", "Tess", "Ruby"], all_names); + /// + /// Ok(()) + /// })?; + /// + /// conn.transaction::<(), _, _>(|| { + /// diesel::insert_into(users) + /// .values(name.eq("Pascal")) + /// .execute(&conn)?; + /// + /// let all_names = users.select(name).load::(&conn)?; + /// assert_eq!(vec!["Sean", "Tess", "Ruby", "Pascal"], all_names); + /// + /// // If we want to roll back the transaction, but don't have an + /// // actual error to return, we can return `RollbackTransaction`. + /// Err(Error::RollbackTransaction) + /// }); + /// + /// let all_names = users.select(name).load::(&conn)?; + /// assert_eq!(vec!["Sean", "Tess", "Ruby"], all_names); + /// # Ok(()) + /// # } + /// ``` + fn transaction(&self, f: F) -> Result + where + F: FnOnce() -> Result, + E: From, + { + let transaction_manager = self.transaction_manager(); + transaction_manager.begin_transaction(self)?; + match f() { + Ok(value) => { + transaction_manager.commit_transaction(self)?; + Ok(value) + } + Err(e) => { + transaction_manager.rollback_transaction(self)?; + Err(e) + } + } + } + + /// Creates a transaction that will never be committed. This is useful for + /// tests. Panics if called while inside of a transaction. + fn begin_test_transaction(&self) -> QueryResult<()> { + let transaction_manager = self.transaction_manager(); + assert_eq!(transaction_manager.get_transaction_depth(), 0); + transaction_manager.begin_transaction(self) + } + + /// Executes the given function inside a transaction, but does not commit + /// it. Panics if the given function returns an error. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// use diesel::result::Error; + /// + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = establish_connection(); + /// conn.test_transaction::<_, Error, _>(|| { + /// diesel::insert_into(users) + /// .values(name.eq("Ruby")) + /// .execute(&conn)?; + /// + /// let all_names = users.select(name).load::(&conn)?; + /// assert_eq!(vec!["Sean", "Tess", "Ruby"], all_names); + /// + /// Ok(()) + /// }); + /// + /// // Even though we returned `Ok`, the transaction wasn't committed. + /// let all_names = users.select(name).load::(&conn)?; + /// assert_eq!(vec!["Sean", "Tess"], all_names); + /// # Ok(()) + /// # } + /// ``` + fn test_transaction(&self, f: F) -> T + where + F: FnOnce() -> Result, + E: Debug, + { + let mut user_result = None; + let _ = self.transaction::<(), _, _>(|| { + user_result = f().ok(); + Err(Error::RollbackTransaction) + }); + user_result.expect("Transaction did not succeed") + } + + #[doc(hidden)] + fn execute(&self, query: &str) -> QueryResult; + + #[doc(hidden)] + fn load(&self, source: T) -> QueryResult> + where + T: AsQuery, + T::Query: QueryFragment + QueryId, + U: FromSqlRow, + Self::Backend: QueryMetadata; + + #[doc(hidden)] + fn execute_returning_count(&self, source: &T) -> QueryResult + where + T: QueryFragment + QueryId; + + #[doc(hidden)] + fn transaction_manager(&self) -> &Self::TransactionManager; +} diff --git a/collector/benchmarks/diesel/diesel/src/connection/statement_cache.rs b/collector/benchmarks/diesel/diesel/src/connection/statement_cache.rs new file mode 100644 index 000000000..614105a57 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/connection/statement_cache.rs @@ -0,0 +1,256 @@ +#![doc(hidden)] +//! A primer on prepared statement caching in Diesel +//! ------------------------------------------------ +//! +//! Diesel uses prepared statements for virtually all queries. This is most +//! visible in our lack of any sort of "quoting" API. Values must always be +//! transmitted as bind parameters, we do not support direct interpolation. The +//! only method in the public API that doesn't require the use of prepared +//! statements is `Connection#batch_execute`. +//! +//! In order to avoid the cost of re-parsing and planning subsequent queries, +//! Diesel caches the prepared statement whenever possible. Queries will fall +//! into one of three buckets: +//! +//! - Unsafe to cache +//! - Cached by SQL +//! - Cached by type +//! +//! A query is considered unsafe to cache if it represents a potentially +//! unbounded number of queries. This is communicated to the connection through +//! `QueryFragment#is_safe_to_cache_prepared`. While this is done as a full AST +//! pass, after monomorphisation and inlining this will usually be optimized to +//! a constant. Only boxed queries will need to do actual work to answer this +//! question. +//! +//! The majority of AST nodes are safe to cache if their components are safe to +//! cache. There are at least 4 cases where a query is unsafe to cache: +//! +//! - queries containing `IN` with bind parameters +//! - This requires 1 bind parameter per value, and is therefore unbounded +//! - `IN` with subselects are cached (assuming the subselect is safe to +//! cache) +//! - `INSERT` statements +//! - The SQL varies based on the number of rows being inserted. We could in +//! theory cache the single row case, but the cost of parsing in a write +//! query is pretty insignificant compared to reads +//! - `UPDATE` statements +//! - Technically it's bounded on "number of optional values being passed to +//! `SET` factorial" but that's still quite high, and not worth caching +//! for the same reason as single row inserts +//! - `SqlLiteral` nodes +//! - We have no way of knowing whether the SQL was generated dynamically or +//! not, so we must assume that it's unbounded +//! +//! For queries which are unsafe to cache, the statement cache will never insert +//! them. They will be prepared and immediately released after use (or in the +//! case of PG they will use the unnamed prepared statement). +//! +//! For statements which are able to be cached, we then have to determine what +//! to use as the cache key. The standard method that virtually all ORMs or +//! database access layers use in the wild is to store the statements in a +//! hash map, using the SQL as the key. +//! +//! However, the majority of queries using Diesel that are safe to cache as +//! prepared statements will be uniquely identified by their type. For these +//! queries, we can bypass the query builder entirely. Since our AST is +//! generally optimized away by the compiler, for these queries the cost of +//! fetching a prepared statement from the cache is the cost of `HashMap::get`, where the key we're fetching by is a compile time constant. For +//! these types, the AST pass to gather the bind parameters will also be +//! optimized to accessing each parameter individually. +//! +//! Determining if a query can be cached by type is the responsibility of the +//! `QueryId` trait. This trait is quite similar to `Any`, but with a few +//! differences: +//! +//! - No `'static` bound +//! - Something being a reference never changes the SQL that is generated, +//! so `&T` has the same query id as `T`. +//! - `Option` instead of `TypeId` +//! - We need to be able to constrain on this trait being implemented, but +//! not all types will actually have a static query id. Hopefully once +//! specialization is stable we can remove the `QueryId` bound and +//! specialize on it instead (or provide a blanket impl for all `T`) +//! - Implementors give a more broad type than `Self` +//! - This really only affects bind parameters. There are 6 different Rust +//! types which can be used for a parameter of type `timestamp`. The same +//! statement can be used regardless of the Rust type, so `Bound` +//! defines its `QueryId` as `Bound`. +//! +//! A type returning `Some(id)` or `None` for its query ID is based on whether +//! the SQL it generates can change without the type changing. At the moment, +//! the only type which is safe to cache as a prepared statement but does not +//! have a static query ID is something which has been boxed. +//! +//! One potential optimization that we don't perform is storing the queries +//! which are cached by type ID in a separate map. Since a type ID is a u64, +//! this would allow us to use a specialized map which knows that there will +//! never be hashing collisions (also known as a perfect hashing function), +//! which would mean lookups are always constant time. However, this would save +//! nanoseconds on an operation that will take microseconds or even +//! milliseconds. + +use std::any::TypeId; +use std::borrow::Cow; +use std::cell::{RefCell, RefMut}; +use std::collections::HashMap; +use std::hash::Hash; +use std::ops::{Deref, DerefMut}; + +use crate::backend::Backend; +use crate::query_builder::*; +use crate::result::QueryResult; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct StatementCache { + pub cache: RefCell, Statement>>, +} + +#[allow(clippy::len_without_is_empty, clippy::new_without_default)] +impl StatementCache +where + DB: Backend, + DB::TypeMetadata: Clone, + DB::QueryBuilder: Default, + StatementCacheKey: Hash + Eq, +{ + pub fn new() -> Self { + StatementCache { + cache: RefCell::new(HashMap::new()), + } + } + + pub fn len(&self) -> usize { + self.cache.borrow().len() + } + + pub fn cached_statement( + &self, + source: &T, + bind_types: &[DB::TypeMetadata], + prepare_fn: F, + ) -> QueryResult> + where + T: QueryFragment + QueryId, + F: FnOnce(&str) -> QueryResult, + { + use std::collections::hash_map::Entry::{Occupied, Vacant}; + + let cache_key = StatementCacheKey::for_source(source, bind_types)?; + + if !source.is_safe_to_cache_prepared()? { + let sql = cache_key.sql(source)?; + return prepare_fn(&sql).map(MaybeCached::CannotCache); + } + + refmut_map_result(self.cache.borrow_mut(), |cache| { + match cache.entry(cache_key) { + Occupied(entry) => Ok(entry.into_mut()), + Vacant(entry) => { + let statement = { + let sql = entry.key().sql(source)?; + prepare_fn(&sql) + }; + + Ok(entry.insert(statement?)) + } + } + }) + .map(MaybeCached::Cached) + } +} + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub enum MaybeCached<'a, T: 'a> { + CannotCache(T), + Cached(RefMut<'a, T>), +} + +impl<'a, T> Deref for MaybeCached<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + match *self { + MaybeCached::CannotCache(ref x) => x, + MaybeCached::Cached(ref x) => &**x, + } + } +} + +impl<'a, T> DerefMut for MaybeCached<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + match *self { + MaybeCached::CannotCache(ref mut x) => x, + MaybeCached::Cached(ref mut x) => &mut **x, + } + } +} + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +#[derive(Hash, PartialEq, Eq)] +pub enum StatementCacheKey { + Type(TypeId), + Sql { + sql: String, + bind_types: Vec, + }, +} + +impl StatementCacheKey +where + DB: Backend, + DB::QueryBuilder: Default, + DB::TypeMetadata: Clone, +{ + pub fn for_source(source: &T, bind_types: &[DB::TypeMetadata]) -> QueryResult + where + T: QueryFragment + QueryId, + { + match T::query_id() { + Some(id) => Ok(StatementCacheKey::Type(id)), + None => { + let sql = Self::construct_sql(source)?; + Ok(StatementCacheKey::Sql { + sql: sql, + bind_types: bind_types.into(), + }) + } + } + } + + pub fn sql>(&self, source: &T) -> QueryResult> { + match *self { + StatementCacheKey::Type(_) => Self::construct_sql(source).map(Cow::Owned), + StatementCacheKey::Sql { ref sql, .. } => Ok(Cow::Borrowed(sql)), + } + } + + fn construct_sql>(source: &T) -> QueryResult { + let mut query_builder = DB::QueryBuilder::default(); + source.to_sql(&mut query_builder)?; + Ok(query_builder.finish()) + } +} + +/// Similar to `RefMut::map`, but for functions which return `Result` +/// +/// If we were in Haskell (and if `RefMut` were `Traversable`), this would just be +/// `traverse`. +fn refmut_map_result(mut refmut: RefMut, mapper: F) -> QueryResult> +where + F: FnOnce(&mut T) -> QueryResult<&mut U>, +{ + // We can't just use `RefMut::map` here, since to lift the error out of that + // closure we'd need to return *something*. + // + // Instead we will very briefly convert to a raw pointer to eliminate + // lifetimes from the equation. Ultimately the cast is safe since the input + // and output lifetimes are identical. However, without the raw pointer + // we would have two live mutable references at the same time. + let ptr = mapper(&mut *refmut).map(|x| x as *mut _)?; + Ok(RefMut::map(refmut, |_| unsafe { &mut *ptr })) +} diff --git a/collector/benchmarks/diesel/diesel/src/connection/transaction_manager.rs b/collector/benchmarks/diesel/diesel/src/connection/transaction_manager.rs new file mode 100644 index 000000000..746afb19b --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/connection/transaction_manager.rs @@ -0,0 +1,247 @@ +use crate::backend::UsesAnsiSavepointSyntax; +use crate::connection::{Connection, SimpleConnection}; +use crate::result::{DatabaseErrorKind, Error, QueryResult}; + +/// Manages the internal transaction state for a connection. +/// +/// You will not need to interact with this trait, unless you are writing an +/// implementation of [`Connection`](trait.Connection.html). +pub trait TransactionManager { + /// Begin a new transaction or savepoint + /// + /// If the transaction depth is greater than 0, + /// this should create a savepoint instead. + /// This function is expected to increment the transaction depth by 1. + fn begin_transaction(&self, conn: &Conn) -> QueryResult<()>; + + /// Rollback the inner-most transaction or savepoint + /// + /// If the transaction depth is greater than 1, + /// this should rollback to the most recent savepoint. + /// This function is expected to decrement the transaction depth by 1. + fn rollback_transaction(&self, conn: &Conn) -> QueryResult<()>; + + /// Commit the inner-most transaction or savepoint + /// + /// If the transaction depth is greater than 1, + /// this should release the most recent savepoint. + /// This function is expected to decrement the transaction depth by 1. + fn commit_transaction(&self, conn: &Conn) -> QueryResult<()>; + + /// Fetch the current transaction depth + /// + /// Used to ensure that `begin_test_transaction` is not called when already + /// inside of a transaction. + fn get_transaction_depth(&self) -> u32; +} + +use std::cell::Cell; + +/// An implementation of `TransactionManager` which can be used for backends +/// which use ANSI standard syntax for savepoints such as SQLite and PostgreSQL. +#[allow(missing_debug_implementations)] +#[derive(Default)] +pub struct AnsiTransactionManager { + transaction_depth: Cell, +} + +impl AnsiTransactionManager { + /// Create a new transaction manager + pub fn new() -> Self { + AnsiTransactionManager::default() + } + + fn change_transaction_depth(&self, by: i32, query: QueryResult<()>) -> QueryResult<()> { + if query.is_ok() { + self.transaction_depth + .set(self.transaction_depth.get() + by) + } + query + } + + /// Begin a transaction with custom SQL + /// + /// This is used by connections to implement more complex transaction APIs + /// to set things such as isolation levels. + /// Returns an error if already inside of a transaction. + pub fn begin_transaction_sql(&self, conn: &Conn, sql: &str) -> QueryResult<()> + where + Conn: SimpleConnection, + { + use crate::result::Error::AlreadyInTransaction; + + if self.transaction_depth.get() == 0 { + self.change_transaction_depth(1, conn.batch_execute(sql)) + } else { + Err(AlreadyInTransaction) + } + } +} + +impl TransactionManager for AnsiTransactionManager +where + Conn: Connection, + Conn::Backend: UsesAnsiSavepointSyntax, +{ + fn begin_transaction(&self, conn: &Conn) -> QueryResult<()> { + let transaction_depth = self.transaction_depth.get(); + self.change_transaction_depth( + 1, + if transaction_depth == 0 { + conn.batch_execute("BEGIN") + } else { + conn.batch_execute(&format!("SAVEPOINT diesel_savepoint_{}", transaction_depth)) + }, + ) + } + + fn rollback_transaction(&self, conn: &Conn) -> QueryResult<()> { + let transaction_depth = self.transaction_depth.get(); + self.change_transaction_depth( + -1, + if transaction_depth == 1 { + conn.batch_execute("ROLLBACK") + } else { + conn.batch_execute(&format!( + "ROLLBACK TO SAVEPOINT diesel_savepoint_{}", + transaction_depth - 1 + )) + }, + ) + } + + /// If the transaction fails to commit due to a `SerializationFailure` or a + /// `ReadOnlyTransaction` a rollback will be attempted. If the rollback succeeds, + /// the original error will be returned, otherwise the error generated by the rollback + /// will be returned. In the second case the connection should be considered broken + /// as it contains a uncommitted unabortable open transaction. + fn commit_transaction(&self, conn: &Conn) -> QueryResult<()> { + let transaction_depth = self.transaction_depth.get(); + if transaction_depth <= 1 { + match conn.batch_execute("COMMIT") { + // When any of these kinds of error happen on `COMMIT`, it is expected + // that a `ROLLBACK` would succeed, leaving the transaction in a non-broken state. + // If there are other such errors, it is fine to add them here. + e @ Err(Error::DatabaseError(DatabaseErrorKind::SerializationFailure, _)) + | e @ Err(Error::DatabaseError(DatabaseErrorKind::ReadOnlyTransaction, _)) => { + self.change_transaction_depth(-1, conn.batch_execute("ROLLBACK"))?; + e + } + result => self.change_transaction_depth(-1, result), + } + } else { + self.change_transaction_depth( + -1, + conn.batch_execute(&format!( + "RELEASE SAVEPOINT diesel_savepoint_{}", + transaction_depth - 1 + )), + ) + } + } + + fn get_transaction_depth(&self) -> u32 { + self.transaction_depth.get() as u32 + } +} + +#[cfg(test)] +mod test { + #[cfg(feature = "postgres")] + macro_rules! matches { + ($expression:expr, $( $pattern:pat )|+ $( if $guard: expr )?) => { + match $expression { + $( $pattern )|+ $( if $guard )? => true, + _ => false + } + } + } + + #[test] + #[cfg(feature = "postgres")] + fn transaction_depth_is_tracked_properly_on_commit_failure() { + use crate::result::DatabaseErrorKind::SerializationFailure; + use crate::result::Error::DatabaseError; + use crate::*; + use std::sync::{Arc, Barrier}; + use std::thread; + + table! { + #[sql_name = "transaction_depth_is_tracked_properly_on_commit_failure"] + serialization_example { + id -> Serial, + class -> Integer, + } + } + + let conn = crate::test_helpers::pg_connection_no_transaction(); + + sql_query("DROP TABLE IF EXISTS transaction_depth_is_tracked_properly_on_commit_failure;") + .execute(&conn) + .unwrap(); + sql_query( + r#" + CREATE TABLE transaction_depth_is_tracked_properly_on_commit_failure ( + id SERIAL PRIMARY KEY, + class INTEGER NOT NULL + ) + "#, + ) + .execute(&conn) + .unwrap(); + + insert_into(serialization_example::table) + .values(&vec![ + serialization_example::class.eq(1), + serialization_example::class.eq(2), + ]) + .execute(&conn) + .unwrap(); + + let barrier = Arc::new(Barrier::new(2)); + let threads = (1..3) + .map(|i| { + let barrier = barrier.clone(); + thread::spawn(move || { + use crate::connection::transaction_manager::AnsiTransactionManager; + use crate::connection::transaction_manager::TransactionManager; + let conn = crate::test_helpers::pg_connection_no_transaction(); + assert_eq!(0, >::get_transaction_depth(&conn.transaction_manager)); + + let result = + conn.build_transaction().serializable().run(|| { + assert_eq!(1, >::get_transaction_depth(&conn.transaction_manager)); + + let _ = serialization_example::table + .filter(serialization_example::class.eq(i)) + .count() + .execute(&conn)?; + + barrier.wait(); + + let other_i = if i == 1 { 2 } else { 1 }; + insert_into(serialization_example::table) + .values(serialization_example::class.eq(other_i)) + .execute(&conn) + }); + + assert_eq!(0, >::get_transaction_depth(&conn.transaction_manager)); + result + }) + }) + .collect::>(); + + let mut results = threads + .into_iter() + .map(|t| t.join().unwrap()) + .collect::>(); + + results.sort_by_key(|r| r.is_err()); + + assert!(matches!(results[0], Ok(_))); + assert!(matches!( + results[1], + Err(DatabaseError(SerializationFailure, _)) + )); + } +} diff --git a/collector/benchmarks/diesel/diesel/src/data_types.rs b/collector/benchmarks/diesel/diesel/src/data_types.rs new file mode 100644 index 000000000..43018e5a1 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/data_types.rs @@ -0,0 +1,7 @@ +//! Structs to represent the primitive equivalent of SQL types where +//! there is no existing Rust primitive, or where using it would be +//! confusing (such as date and time types). This module will re-export +//! all backend specific data structures when compiled against that +//! backend. +#[cfg(feature = "postgres")] +pub use crate::pg::data_types::*; diff --git a/collector/benchmarks/diesel/diesel/src/deserialize.rs b/collector/benchmarks/diesel/diesel/src/deserialize.rs new file mode 100644 index 000000000..4994e5a5d --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/deserialize.rs @@ -0,0 +1,442 @@ +//! Types and traits related to deserializing values from the database + +use std::error::Error; +use std::result; + +use crate::backend::{self, Backend}; +use crate::row::{NamedRow, Row}; +use crate::sql_types::{SingleValue, SqlType, Untyped}; + +/// A specialized result type representing the result of deserializing +/// a value from the database. +pub type Result = result::Result>; + +/// Trait indicating that a record can be queried from the database. +/// +/// Types which implement `Queryable` represent the result of a SQL query. This +/// does not necessarily mean they represent a single database table. +/// +/// Diesel represents the return type of a query as a tuple. The purpose of this +/// trait is to convert from a tuple of Rust values that have been deserialized +/// into your struct. +/// +/// This trait can be [derived](derive.Queryable.html) +/// +/// # Examples +/// +/// If we just want to map a query to our struct, we can use `derive`. +/// +/// ```rust +/// # include!("doctest_setup.rs"); +/// # +/// #[derive(Queryable, PartialEq, Debug)] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # let connection = establish_connection(); +/// let first_user = users.first(&connection)?; +/// let expected = User { id: 1, name: "Sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +/// +/// If we want to do additional work during deserialization, we can use +/// `deserialize_as` to use a different implementation. +/// +/// ```rust +/// # include!("doctest_setup.rs"); +/// # +/// # use schema::users; +/// # use diesel::backend::{self, Backend}; +/// # use diesel::deserialize::{Queryable, FromSql}; +/// # use diesel::sql_types::Text; +/// # +/// struct LowercaseString(String); +/// +/// impl Into for LowercaseString { +/// fn into(self) -> String { +/// self.0 +/// } +/// } +/// +/// impl Queryable for LowercaseString +/// where +/// DB: Backend, +/// String: FromSql, +/// { +/// type Row = String; +/// +/// fn build(s: String) -> Self { +/// LowercaseString(s.to_lowercase()) +/// } +/// } +/// +/// #[derive(Queryable, PartialEq, Debug)] +/// struct User { +/// id: i32, +/// #[diesel(deserialize_as = "LowercaseString")] +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # let connection = establish_connection(); +/// let first_user = users.first(&connection)?; +/// let expected = User { id: 1, name: "sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +/// +/// Alternatively, we can implement the trait for our struct manually. +/// +/// ```rust +/// # include!("doctest_setup.rs"); +/// # +/// use schema::users; +/// use diesel::deserialize::Queryable; +/// +/// # /* +/// type DB = diesel::sqlite::Sqlite; +/// # */ +/// +/// #[derive(PartialEq, Debug)] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// impl Queryable for User { +/// type Row = (i32, String); +/// +/// fn build(row: Self::Row) -> Self { +/// User { +/// id: row.0, +/// name: row.1.to_lowercase(), +/// } +/// } +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # let connection = establish_connection(); +/// let first_user = users.first(&connection)?; +/// let expected = User { id: 1, name: "sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +pub trait Queryable +where + DB: Backend, +{ + /// The Rust type you'd like to map from. + /// + /// This is typically a tuple of all of your struct's fields. + type Row: FromStaticSqlRow; + + /// Construct an instance of this type + fn build(row: Self::Row) -> Self; +} + +#[doc(inline)] +pub use diesel_derives::Queryable; + +/// Deserializes the result of a query constructed with [`sql_query`]. +/// +/// This trait can be [derived](derive.QueryableByName.html) +/// +/// [`sql_query`]: ../fn.sql_query.html +/// +/// # Examples +/// +/// If we just want to map a query to our struct, we can use `derive`. +/// +/// ```rust +/// # include!("doctest_setup.rs"); +/// # use schema::users; +/// # use diesel::sql_query; +/// # +/// #[derive(QueryableByName, PartialEq, Debug)] +/// #[table_name = "users"] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let connection = establish_connection(); +/// let first_user = sql_query("SELECT * FROM users ORDER BY id LIMIT 1") +/// .get_result(&connection)?; +/// let expected = User { id: 1, name: "Sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +/// +/// If we want to do additional work during deserialization, we can use +/// `deserialize_as` to use a different implementation. +/// +/// ```rust +/// # include!("doctest_setup.rs"); +/// # use diesel::sql_query; +/// # use schema::users; +/// # use diesel::backend::{self, Backend}; +/// # use diesel::deserialize::{self, FromSql}; +/// # +/// struct LowercaseString(String); +/// +/// impl Into for LowercaseString { +/// fn into(self) -> String { +/// self.0 +/// } +/// } +/// +/// impl FromSql for LowercaseString +/// where +/// DB: Backend, +/// String: FromSql, +/// { +/// fn from_sql(bytes: backend::RawValue) -> deserialize::Result { +/// String::from_sql(bytes) +/// .map(|s| LowercaseString(s.to_lowercase())) +/// } +/// } +/// +/// #[derive(QueryableByName, PartialEq, Debug)] +/// #[table_name = "users"] +/// struct User { +/// id: i32, +/// #[diesel(deserialize_as = "LowercaseString")] +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let connection = establish_connection(); +/// let first_user = sql_query("SELECT * FROM users ORDER BY id LIMIT 1") +/// .get_result(&connection)?; +/// let expected = User { id: 1, name: "sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +pub trait QueryableByName +where + Self: Sized, + DB: Backend, +{ + /// Construct an instance of `Self` from the database row + fn build<'a>(row: &impl NamedRow<'a, DB>) -> Result; +} + +#[doc(inline)] +pub use diesel_derives::QueryableByName; + +/// Deserialize a single field of a given SQL type. +/// +/// When possible, implementations of this trait should prefer to use an +/// existing implementation, rather than reading from `bytes`. (For example, if +/// you are implementing this for an enum which is represented as an integer in +/// the database, prefer `i32::from_sql(bytes)` (or the explicit form +/// `>::from_sql(bytes)`) over reading from `bytes` +/// directly) +/// +/// Types which implement this trait should also have `#[derive(FromSqlRow)]` +/// +/// ### Backend specific details +/// +/// - For PostgreSQL, the bytes will be sent using the binary protocol, not text. +/// - For SQLite, the actual type of `DB::RawValue` is private API. All +/// implementations of this trait must be written in terms of an existing +/// primitive. +/// - For MySQL, the value of `bytes` will depend on the return value of +/// `type_metadata` for the given SQL type. See [`MysqlType`] for details. +/// - For third party backends, consult that backend's documentation. +/// +/// [`MysqlType`]: ../mysql/enum.MysqlType.html +/// +/// ### Examples +/// +/// Most implementations of this trait will be defined in terms of an existing +/// implementation. +/// +/// ```rust +/// # use diesel::backend::{self, Backend}; +/// # use diesel::sql_types::*; +/// # use diesel::deserialize::{self, FromSql, FromSqlRow}; +/// # +/// #[repr(i32)] +/// #[derive(Debug, Clone, Copy, FromSqlRow)] +/// pub enum MyEnum { +/// A = 1, +/// B = 2, +/// } +/// +/// impl FromSql for MyEnum +/// where +/// DB: Backend, +/// i32: FromSql, +/// { +/// fn from_sql(bytes: backend::RawValue) -> deserialize::Result { +/// match i32::from_sql(bytes)? { +/// 1 => Ok(MyEnum::A), +/// 2 => Ok(MyEnum::B), +/// x => Err(format!("Unrecognized variant {}", x).into()), +/// } +/// } +/// } +/// ``` +pub trait FromSql: Sized { + /// See the trait documentation. + fn from_sql(bytes: backend::RawValue) -> Result; + + /// A specialized variant of `from_sql` for handling null values. + /// + /// The default implementation returns an `UnexpectedNullError` for + /// an encountered null value and calls `Self::from_sql` otherwise + /// + /// If your custom type supports null values you need to provide a + /// custom implementation. + #[inline(always)] + fn from_nullable_sql(bytes: Option>) -> Result { + match bytes { + Some(bytes) => Self::from_sql(bytes), + None => Err(Box::new(crate::result::UnexpectedNullError)), + } + } +} + +/// Deserialize a database row into a rust data structure +/// +/// Diesel provides wild card implementations of this trait for all types +/// that implement one of the following traits: +/// * [`Queryable`](trait.Queryable.html) +/// * [`QueryableByName`](trait.QueryableByName.html) +pub trait FromSqlRow: Sized { + /// See the trait documentation. + fn build_from_row<'a>(row: &impl Row<'a, DB>) -> Result; +} + +#[doc(inline)] +pub use diesel_derives::FromSqlRow; + +/// A marker trait indicating that the corresponding type consumes a static at +/// compile time known number of field +/// +/// There is normally no need to implement this trait. Diesel provides +/// wild card impls for all types that implement `FromSql` or `Queryable` +/// where the size of `ST` is known +pub trait StaticallySizedRow: FromSqlRow { + /// The number of fields that this type will consume. + const FIELD_COUNT: usize; +} + +impl FromSqlRow for T +where + DB: Backend, + T: QueryableByName, +{ + fn build_from_row<'a>(row: &impl Row<'a, DB>) -> Result { + T::build(row) + } +} + +/// A helper trait to deserialize a statically sized row into an tuple +/// +/// **If you see an error message mentioning this trait you likly +/// trying to map the result of an query to an struct with missmatching +/// field types. Recheck your field order and the concrete field types** +/// +/// You should not need to implement this trait directly. +/// Diesel provides wild card implementations for any supported tuple size +/// and for any type that implements `FromSql`. +/// +// This is a distinct trait from `FromSqlRow` because otherwise we +// are getting conflicting implementation errors for our `FromSqlRow` +// implementation for tuples and our wild card impl for all types +// implementing `Queryable` +pub trait FromStaticSqlRow: Sized { + /// See the trait documentation + fn build_from_row<'a>(row: &impl Row<'a, DB>) -> Result; +} + +impl FromSqlRow for T +where + T: Queryable, + ST: SqlType, + DB: Backend, + T::Row: FromStaticSqlRow, +{ + fn build_from_row<'a>(row: &impl Row<'a, DB>) -> Result { + let row = >::build_from_row(row)?; + Ok(T::build(row)) + } +} + +impl FromStaticSqlRow for T +where + DB: Backend, + T: FromSql, + ST: SingleValue, +{ + fn build_from_row<'a>(row: &impl Row<'a, DB>) -> Result { + use crate::row::Field; + + let field = row.get(0).ok_or(crate::result::UnexpectedEndOfRow)?; + T::from_nullable_sql(field.value()) + } +} + +// We cannot have this impl because rustc +// then complains in third party crates that +// diesel may implement `SingleValue` for tuples +// in the future. While that is theoretically true, +// that will likly not happen in practice. +// If we get negative trait impls at some point in time +// it should be possible to make this work. +/*impl Queryable for T +where + DB: Backend, + T: FromStaticSqlRow, + ST: SingleValue, +{ + type Row = Self; + + fn build(row: Self::Row) -> Self { + row + } +}*/ + +impl StaticallySizedRow for T +where + ST: SqlType + crate::util::TupleSize, + T: Queryable, + DB: Backend, +{ + const FIELD_COUNT: usize = ::SIZE; +} diff --git a/collector/benchmarks/diesel/diesel/src/doctest_setup.rs b/collector/benchmarks/diesel/diesel/src/doctest_setup.rs new file mode 100644 index 000000000..0dfce7272 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/doctest_setup.rs @@ -0,0 +1,232 @@ +use diesel::prelude::*; +use dotenv::dotenv; + +cfg_if::cfg_if! { + if #[cfg(feature = "postgres")] { + #[allow(dead_code)] + type DB = diesel::pg::Pg; + + fn connection_no_transaction() -> PgConnection { + let connection_url = database_url_from_env("PG_DATABASE_URL"); + PgConnection::establish(&connection_url).unwrap() + } + + fn connection_no_data() -> PgConnection { + let connection = connection_no_transaction(); + connection.begin_test_transaction().unwrap(); + connection.execute("DROP TABLE IF EXISTS users CASCADE").unwrap(); + connection.execute("DROP TABLE IF EXISTS animals CASCADE").unwrap(); + connection.execute("DROP TABLE IF EXISTS posts CASCADE").unwrap(); + connection.execute("DROP TABLE IF EXISTS comments CASCADE").unwrap(); + + connection + } + + #[allow(dead_code)] + fn establish_connection() -> PgConnection { + let connection = connection_no_data(); + + connection.execute("CREATE TABLE users ( + id SERIAL PRIMARY KEY, + name VARCHAR NOT NULL + )").unwrap(); + connection.execute("INSERT INTO users (name) VALUES ('Sean'), ('Tess')").unwrap(); + + connection.execute("CREATE TABLE animals ( + id SERIAL PRIMARY KEY, + species VARCHAR NOT NULL, + legs INTEGER NOT NULL, + name VARCHAR + )").unwrap(); + connection.execute("INSERT INTO animals (species, legs, name) VALUES + ('dog', 4, 'Jack'), + ('spider', 8, null)").unwrap(); + + connection.execute("CREATE TABLE posts ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + title VARCHAR NOT NULL + )").unwrap(); + connection.execute("INSERT INTO posts (user_id, title) VALUES + (1, 'My first post'), + (1, 'About Rust'), + (2, 'My first post too')").unwrap(); + + connection.execute("CREATE TABLE comments ( + id SERIAL PRIMARY KEY, + post_id INTEGER NOT NULL, + body VARCHAR NOT NULL + )").unwrap(); + connection.execute("INSERT INTO comments (post_id, body) VALUES + (1, 'Great post'), + (2, 'Yay! I am learning Rust'), + (3, 'I enjoyed your post')").unwrap(); + + connection + } + } else if #[cfg(feature = "sqlite")] { + #[allow(dead_code)] + type DB = diesel::sqlite::Sqlite; + + fn connection_no_data() -> SqliteConnection { + SqliteConnection::establish(":memory:").unwrap() + } + + #[allow(dead_code)] + fn establish_connection() -> SqliteConnection { + let connection = connection_no_data(); + + connection.execute("CREATE TABLE users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name VARCHAR NOT NULL + )").unwrap(); + connection.execute("INSERT INTO users (name) VALUES ('Sean'), ('Tess')").unwrap(); + + connection.execute("CREATE TABLE animals ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + species VARCHAR NOT NULL, + legs INTEGER NOT NULL, + name VARCHAR + )").unwrap(); + connection.execute("INSERT INTO animals (species, legs, name) VALUES + ('dog', 4, 'Jack'), + ('spider', 8, null)").unwrap(); + + connection.execute("CREATE TABLE posts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + title VARCHAR NOT NULL + )").unwrap(); + connection.execute("INSERT INTO posts (user_id, title) VALUES + (1, 'My first post'), + (1, 'About Rust'), + (2, 'My first post too')").unwrap(); + + connection.execute("CREATE TABLE comments ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + post_id INTEGER NOT NULL, + body VARCHAR NOT NULL + )").unwrap(); + connection.execute("INSERT INTO comments (post_id, body) VALUES + (1, 'Great post'), + (2, 'Yay! I am learning Rust'), + (3, 'I enjoyed your post')").unwrap(); + + connection + } + } else if #[cfg(feature = "mysql")] { + #[allow(dead_code)] + type DB = diesel::mysql::Mysql; + + fn connection_no_data() -> MysqlConnection { + let connection_url = database_url_from_env("MYSQL_UNIT_TEST_DATABASE_URL"); + let connection = MysqlConnection::establish(&connection_url).unwrap(); + connection.execute("DROP TABLE IF EXISTS users CASCADE").unwrap(); + connection.execute("DROP TABLE IF EXISTS animals CASCADE").unwrap(); + connection.execute("DROP TABLE IF EXISTS posts CASCADE").unwrap(); + connection.execute("DROP TABLE IF EXISTS comments CASCADE").unwrap(); + + connection + } + + #[allow(dead_code)] + fn establish_connection() -> MysqlConnection { + let connection = connection_no_data(); + + connection.execute("CREATE TABLE users ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + name TEXT NOT NULL + ) CHARACTER SET utf8mb4").unwrap(); + connection.execute("INSERT INTO users (name) VALUES ('Sean'), ('Tess')").unwrap(); + + connection.execute("CREATE TABLE animals ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + species TEXT NOT NULL, + legs INTEGER NOT NULL, + name TEXT + ) CHARACTER SET utf8mb4").unwrap(); + connection.execute("INSERT INTO animals (species, legs, name) VALUES + ('dog', 4, 'Jack'), + ('spider', 8, null)").unwrap(); + + connection.execute("CREATE TABLE posts ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + user_id INTEGER NOT NULL, + title TEXT NOT NULL + ) CHARACTER SET utf8mb4").unwrap(); + connection.execute("INSERT INTO posts (user_id, title) VALUES + (1, 'My first post'), + (1, 'About Rust'), + (2, 'My first post too')").unwrap(); + + connection.execute("CREATE TABLE comments ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + post_id INTEGER NOT NULL, + body TEXT NOT NULL + ) CHARACTER SET utf8mb4").unwrap(); + connection.execute("INSERT INTO comments (post_id, body) VALUES + (1, 'Great post'), + (2, 'Yay! I am learning Rust'), + (3, 'I enjoyed your post')").unwrap(); + + connection.begin_test_transaction().unwrap(); + connection + } + } else { + compile_error!( + "At least one backend must be used to test this crate.\n \ + Pass argument `--features \"\"` with one or more of the following backends, \ + 'mysql', 'postgres', or 'sqlite'. \n\n \ + ex. cargo test --features \"mysql postgres sqlite\"\n" + ); + } +} + +fn database_url_from_env(backend_specific_env_var: &str) -> String { + use std::env; + + dotenv().ok(); + + env::var(backend_specific_env_var) + .or_else(|_| env::var("DATABASE_URL")) + .expect("DATABASE_URL must be set in order to run tests") +} + +mod schema { + use diesel::prelude::*; + + table! { + animals { + id -> Integer, + species -> VarChar, + legs -> Integer, + name -> Nullable, + } + } + + table! { + comments { + id -> Integer, + post_id -> Integer, + body -> VarChar, + } + } + + table! { + posts { + id -> Integer, + user_id -> Integer, + title -> VarChar, + } + } + + table! { + users { + id -> Integer, + name -> VarChar, + } + } + + joinable!(posts -> users (user_id)); + allow_tables_to_appear_in_same_query!(animals, comments, posts, users); +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/array_comparison.rs b/collector/benchmarks/diesel/diesel/src/expression/array_comparison.rs new file mode 100644 index 000000000..83a47ca41 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/array_comparison.rs @@ -0,0 +1,199 @@ +use crate::backend::Backend; +use crate::expression::subselect::Subselect; +use crate::expression::*; +use crate::query_builder::*; +use crate::query_builder::{BoxedSelectStatement, SelectStatement}; +use crate::result::QueryResult; +use crate::sql_types::Bool; + +#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] +pub struct In { + left: T, + values: U, +} + +#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] +pub struct NotIn { + left: T, + values: U, +} + +impl In { + pub fn new(left: T, values: U) -> Self { + In { + left: left, + values: values, + } + } +} + +impl NotIn { + pub fn new(left: T, values: U) -> Self { + NotIn { + left: left, + values: values, + } + } +} + +impl Expression for In +where + T: Expression, + U: Expression, +{ + type SqlType = Bool; +} + +impl Expression for NotIn +where + T: Expression, + U: Expression, +{ + type SqlType = Bool; +} + +impl QueryFragment for In +where + DB: Backend, + T: QueryFragment, + U: QueryFragment + MaybeEmpty, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + if self.values.is_empty() { + out.push_sql("1=0"); + } else { + self.left.walk_ast(out.reborrow())?; + out.push_sql(" IN ("); + self.values.walk_ast(out.reborrow())?; + out.push_sql(")"); + } + Ok(()) + } +} + +impl QueryFragment for NotIn +where + DB: Backend, + T: QueryFragment, + U: QueryFragment + MaybeEmpty, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + if self.values.is_empty() { + out.push_sql("1=1"); + } else { + self.left.walk_ast(out.reborrow())?; + out.push_sql(" NOT IN ("); + self.values.walk_ast(out.reborrow())?; + out.push_sql(")"); + } + Ok(()) + } +} + +impl_selectable_expression!(In); +impl_selectable_expression!(NotIn); + +pub trait AsInExpression { + type InExpression: MaybeEmpty + Expression; + + fn as_in_expression(self) -> Self::InExpression; +} + +impl AsInExpression for I +where + I: IntoIterator, + T: AsExpression, + ST: SqlType + TypedExpressionType, +{ + type InExpression = Many; + + fn as_in_expression(self) -> Self::InExpression { + let expressions = self + .into_iter() + .map(>::as_expression) + .collect(); + Many(expressions) + } +} + +pub trait MaybeEmpty { + fn is_empty(&self) -> bool; +} + +impl AsInExpression for SelectStatement +where + ST: SqlType + TypedExpressionType, + Subselect: Expression, + Self: SelectQuery, +{ + type InExpression = Subselect; + + fn as_in_expression(self) -> Self::InExpression { + Subselect::new(self) + } +} + +impl<'a, ST, QS, DB> AsInExpression for BoxedSelectStatement<'a, ST, QS, DB> +where + ST: SqlType + TypedExpressionType, + Subselect, ST>: Expression, +{ + type InExpression = Subselect; + + fn as_in_expression(self) -> Self::InExpression { + Subselect::new(self) + } +} + +#[derive(Debug, Clone, ValidGrouping)] +pub struct Many(Vec); + +impl Expression for Many { + type SqlType = T::SqlType; +} + +impl MaybeEmpty for Many { + fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +impl SelectableExpression for Many +where + Many: AppearsOnTable, + T: SelectableExpression, +{ +} + +impl AppearsOnTable for Many +where + Many: Expression, + T: AppearsOnTable, +{ +} + +impl QueryFragment for Many +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + let mut first = true; + for value in &self.0 { + if first { + first = false; + } else { + out.push_sql(", "); + } + value.walk_ast(out.reborrow())?; + } + Ok(()) + } +} + +impl QueryId for Many { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/bound.rs b/collector/benchmarks/diesel/diesel/src/expression/bound.rs new file mode 100644 index 000000000..6c3d33d97 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/bound.rs @@ -0,0 +1,55 @@ +use std::marker::PhantomData; + +use super::*; +use crate::backend::Backend; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::serialize::ToSql; +use crate::sql_types::{DieselNumericOps, HasSqlType, SqlType}; + +#[derive(Debug, Clone, Copy, DieselNumericOps)] +pub struct Bound { + item: U, + _marker: PhantomData, +} + +impl Bound { + pub fn new(item: U) -> Self { + Bound { + item: item, + _marker: PhantomData, + } + } +} + +impl Expression for Bound +where + T: SqlType + TypedExpressionType, +{ + type SqlType = T; +} + +impl QueryFragment for Bound +where + DB: Backend + HasSqlType, + U: ToSql, +{ + fn walk_ast(&self, mut pass: AstPass) -> QueryResult<()> { + pass.push_bind_param(&self.item)?; + Ok(()) + } +} + +impl QueryId for Bound { + type QueryId = Bound; + + const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID; +} + +impl SelectableExpression for Bound where Bound: AppearsOnTable {} + +impl AppearsOnTable for Bound where Bound: Expression {} + +impl ValidGrouping for Bound { + type IsAggregate = is_aggregate::Never; +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/coerce.rs b/collector/benchmarks/diesel/diesel/src/expression/coerce.rs new file mode 100644 index 000000000..ecf3eacc5 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/coerce.rs @@ -0,0 +1,73 @@ +use std::marker::PhantomData; + +use crate::backend::Backend; +use crate::expression::*; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types::{DieselNumericOps, SqlType}; + +#[derive(Debug, Copy, Clone, QueryId, DieselNumericOps)] +#[doc(hidden)] +/// Coerces an expression to be another type. No checks are performed to ensure +/// that the new type is valid in all positions that the previous type was. +/// This does not perform an actual cast, it just lies to our type system. +/// +/// This is used for a few expressions where we know that the types are actually +/// always interchangeable. (Examples of this include `Timestamp` vs +/// `Timestamptz`, `VarChar` vs `Text`, and `Json` vs `Jsonb`). +/// +/// This struct should not be considered a general solution to equivalent types. +/// It is a short term workaround for expressions which are known to be commonly +/// used. +pub struct Coerce { + expr: T, + _marker: PhantomData, +} + +impl Coerce { + pub fn new(expr: T) -> Self { + Coerce { + expr: expr, + _marker: PhantomData, + } + } +} + +impl Expression for Coerce +where + T: Expression, + ST: SqlType + TypedExpressionType, +{ + type SqlType = ST; +} + +impl SelectableExpression for Coerce +where + T: SelectableExpression, + Self: Expression, +{ +} + +impl AppearsOnTable for Coerce +where + T: AppearsOnTable, + Self: Expression, +{ +} + +impl QueryFragment for Coerce +where + T: QueryFragment, + DB: Backend, +{ + fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + self.expr.walk_ast(pass) + } +} + +impl ValidGrouping for Coerce +where + T: ValidGrouping, +{ + type IsAggregate = T::IsAggregate; +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/count.rs b/collector/benchmarks/diesel/diesel/src/expression/count.rs new file mode 100644 index 000000000..a941b7cd7 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/count.rs @@ -0,0 +1,73 @@ +use super::functions::sql_function; +use super::{Expression, ValidGrouping}; +use crate::backend::Backend; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types::{BigInt, DieselNumericOps, Nullable, SingleValue, SqlType}; + +sql_function! { + /// Creates a SQL `COUNT` expression + /// + /// As with most bare functions, this is not exported by default. You can import + /// it specifically as `diesel::dsl::count`, or glob import + /// `diesel::dsl::*` + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use diesel::dsl::*; + /// # + /// # fn main() { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// assert_eq!(Ok(1), animals.select(count(name)).first(&connection)); + /// # } + /// ``` + #[aggregate] + fn count(expr: Nullable) -> BigInt; +} + +/// Creates a SQL `COUNT(*)` expression +/// +/// For selecting the count of a query, and nothing else, you can just call +/// [`count`](../query_dsl/trait.QueryDsl.html#method.count) +/// on the query instead. +/// +/// As with most bare functions, this is not exported by default. You can import +/// it specifically as `diesel::dsl::count_star`, or glob import +/// `diesel::dsl::*` +/// +/// # Examples +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use diesel::dsl::*; +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// # let connection = establish_connection(); +/// assert_eq!(Ok(2), users.select(count_star()).first(&connection)); +/// # } +/// ``` +pub fn count_star() -> CountStar { + CountStar +} + +#[derive(Debug, Clone, Copy, QueryId, DieselNumericOps, ValidGrouping)] +#[diesel(aggregate)] +#[doc(hidden)] +pub struct CountStar; + +impl Expression for CountStar { + type SqlType = BigInt; +} + +impl QueryFragment for CountStar { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("COUNT(*)"); + Ok(()) + } +} + +impl_selectable_expression!(CountStar); diff --git a/collector/benchmarks/diesel/diesel/src/expression/exists.rs b/collector/benchmarks/diesel/diesel/src/expression/exists.rs new file mode 100644 index 000000000..385065f6b --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/exists.rs @@ -0,0 +1,92 @@ +use crate::backend::Backend; +use crate::expression::subselect::Subselect; +use crate::expression::{AppearsOnTable, Expression, SelectableExpression, ValidGrouping}; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types::Bool; + +/// Creates a SQL `EXISTS` expression. +/// +/// The argument must be a complete SQL query. The query may reference columns +/// from the outer table. +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// # use diesel::select; +/// # use diesel::dsl::exists; +/// # let connection = establish_connection(); +/// let sean_exists = select(exists(users.filter(name.eq("Sean")))) +/// .get_result(&connection); +/// let jim_exists = select(exists(users.filter(name.eq("Jim")))) +/// .get_result(&connection); +/// assert_eq!(Ok(true), sean_exists); +/// assert_eq!(Ok(false), jim_exists); +/// # } +/// ``` +pub fn exists(query: T) -> Exists { + Exists(Subselect::new(query)) +} + +#[derive(Clone, Copy, QueryId, Debug)] +pub struct Exists(pub Subselect); + +impl Expression for Exists +where + Subselect: Expression, +{ + type SqlType = Bool; +} + +impl ValidGrouping for Exists +where + Subselect: ValidGrouping, +{ + type IsAggregate = as ValidGrouping>::IsAggregate; +} + +#[cfg(not(feature = "unstable"))] +impl QueryFragment for Exists +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("EXISTS ("); + self.0.walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } +} + +#[cfg(feature = "unstable")] +impl QueryFragment for Exists +where + DB: Backend, + T: QueryFragment, +{ + default fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("EXISTS ("); + self.0.walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } +} + +impl SelectableExpression for Exists +where + Self: AppearsOnTable, + Subselect: SelectableExpression, +{ +} + +impl AppearsOnTable for Exists +where + Self: Expression, + Subselect: AppearsOnTable, +{ +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/functions/aggregate_folding.rs b/collector/benchmarks/diesel/diesel/src/expression/functions/aggregate_folding.rs new file mode 100644 index 000000000..1896e21fe --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/functions/aggregate_folding.rs @@ -0,0 +1,68 @@ +use crate::expression::functions::sql_function; +use crate::sql_types::{Foldable, Nullable}; + +sql_function! { + /// Represents a SQL `SUM` function. This function can only take types which are + /// Foldable. + /// + /// # Examples + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # use diesel::dsl::*; + /// # + /// # fn main() { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// assert_eq!(Ok(Some(12i64)), animals.select(sum(legs)).first(&connection)); + /// # } + /// ``` + #[aggregate] + fn sum(expr: Nullable) -> ST::Sum; +} + +sql_function! { + /// Represents a SQL `AVG` function. This function can only take types which are + /// Foldable. + /// + /// # Examples + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # use diesel::dsl::*; + /// # #[cfg(feature = "bigdecimal")] + /// # extern crate bigdecimal; + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # table! { + /// # numbers (number) { + /// # number -> Integer, + /// # } + /// # } + /// # + /// # #[cfg(all(feature = "numeric", any(feature = "postgres", not(feature = "sqlite"))))] + /// # fn run_test() -> QueryResult<()> { + /// # use bigdecimal::BigDecimal; + /// # use self::numbers::dsl::*; + /// # let conn = establish_connection(); + /// # conn.execute("DROP TABLE IF EXISTS numbers")?; + /// # conn.execute("CREATE TABLE numbers (number INTEGER)")?; + /// diesel::insert_into(numbers) + /// .values(&vec![number.eq(1), number.eq(2)]) + /// .execute(&conn)?; + /// let average = numbers.select(avg(number)).get_result(&conn)?; + /// let expected = "1.5".parse::().unwrap(); + /// assert_eq!(Some(expected), average); + /// # Ok(()) + /// # } + /// # + /// # #[cfg(not(all(feature = "numeric", any(feature = "postgres", not(feature = "sqlite")))))] + /// # fn run_test() -> QueryResult<()> { + /// # Ok(()) + /// # } + #[aggregate] + fn avg(expr: Nullable) -> ST::Avg; +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/functions/aggregate_ordering.rs b/collector/benchmarks/diesel/diesel/src/expression/functions/aggregate_ordering.rs new file mode 100644 index 000000000..4b398ce09 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/functions/aggregate_ordering.rs @@ -0,0 +1,52 @@ +use crate::expression::functions::sql_function; +use crate::sql_types::{IntoNullable, Nullable, SingleValue, SqlOrd, SqlType}; + +pub trait SqlOrdAggregate: SingleValue { + type Ret: SqlType + SingleValue; +} + +impl SqlOrdAggregate for ST +where + ST: SqlOrd + SingleValue + IntoNullable, + ST::Nullable: SingleValue, +{ + type Ret = ::Nullable; +} + +sql_function! { + /// Represents a SQL `MAX` function. This function can only take types which are + /// ordered. + /// + /// # Examples + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # use diesel::dsl::*; + /// # + /// # fn main() { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// assert_eq!(Ok(Some(8)), animals.select(max(legs)).first(&connection)); + /// # } + #[aggregate] + fn max(expr: Nullable) -> ST::Ret; +} + +sql_function! { + /// Represents a SQL `MIN` function. This function can only take types which are + /// ordered. + /// + /// # Examples + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # use diesel::dsl::*; + /// # + /// # fn main() { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// assert_eq!(Ok(Some(4)), animals.select(min(legs)).first(&connection)); + /// # } + #[aggregate] + fn min(expr: Nullable) -> ST::Ret; +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/functions/date_and_time.rs b/collector/benchmarks/diesel/diesel/src/expression/functions/date_and_time.rs new file mode 100644 index 000000000..d6cef53d2 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/functions/date_and_time.rs @@ -0,0 +1,87 @@ +use crate::backend::Backend; +use crate::expression::functions::sql_function; +#[cfg(feature = "postgres")] +use crate::expression::{coerce::Coerce, AsExpression}; +use crate::expression::{Expression, ValidGrouping}; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types::*; + +/// Represents the SQL `CURRENT_TIMESTAMP` constant. This is equivalent to the +/// `NOW()` function on backends that support it. +#[allow(non_camel_case_types)] +#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] +pub struct now; + +impl Expression for now { + type SqlType = Timestamp; +} + +impl QueryFragment for now { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("CURRENT_TIMESTAMP"); + Ok(()) + } +} + +impl_selectable_expression!(now); + +operator_allowed!(now, Add, add); +operator_allowed!(now, Sub, sub); +sql_function! { + /// Represents the SQL `DATE` function. The argument should be a Timestamp + /// expression, and the return value will be an expression of type Date. + + /// # Examples + + /// ```ignore + /// # extern crate chrono; + /// # include!("../../doctest_setup.rs"); + /// # use diesel::dsl::*; + /// # + /// # fn main() { + /// # let connection = establish_connection(); + /// let today: chrono::NaiveDate = diesel::select(date(now)).first(&connection).unwrap(); + /// # } + /// ``` + fn date(expr: Timestamp) -> Date; +} + +#[cfg(feature = "postgres")] +impl AsExpression for now { + type Expression = Coerce; + + fn as_expression(self) -> Self::Expression { + Coerce::new(self) + } +} + +#[cfg(feature = "postgres")] +impl AsExpression> for now { + type Expression = Coerce>; + + fn as_expression(self) -> Self::Expression { + Coerce::new(self) + } +} + +/// Represents the SQL `CURRENT_DATE` constant. +#[allow(non_camel_case_types)] +#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] +pub struct today; + +impl Expression for today { + type SqlType = Date; +} + +impl QueryFragment for today { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("CURRENT_DATE"); + Ok(()) + } +} + +impl_selectable_expression!(today); + +operator_allowed!(today, Add, add); +operator_allowed!(today, Sub, sub); diff --git a/collector/benchmarks/diesel/diesel/src/expression/functions/helper_types.rs b/collector/benchmarks/diesel/diesel/src/expression/functions/helper_types.rs new file mode 100644 index 000000000..0ccf8e530 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/functions/helper_types.rs @@ -0,0 +1,21 @@ +#![allow(non_camel_case_types)] + +use crate::dsl::{AsExprOf, SqlTypeOf}; +use crate::expression::grouped::Grouped; +use crate::expression::operators; +use crate::sql_types::{Bool, Nullable}; + +/// The return type of [`not(expr)`](../dsl/fn.not.html) +pub type not = operators::Not>>>; + +/// The return type of [`max(expr)`](../dsl/fn.max.html) +pub type max = super::aggregate_ordering::max::HelperType, Expr>; + +/// The return type of [`min(expr)`](../dsl/fn.min.html) +pub type min = super::aggregate_ordering::min::HelperType, Expr>; + +/// The return type of [`sum(expr)`](../dsl/fn.sum.html) +pub type sum = super::aggregate_folding::sum::HelperType, Expr>; + +/// The return type of [`avg(expr)`](../dsl/fn.avg.html) +pub type avg = super::aggregate_folding::avg::HelperType, Expr>; diff --git a/collector/benchmarks/diesel/diesel/src/expression/functions/mod.rs b/collector/benchmarks/diesel/diesel/src/expression/functions/mod.rs new file mode 100644 index 000000000..154ca87b0 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/functions/mod.rs @@ -0,0 +1,103 @@ +//! Helper macros to define custom sql functions + +#[doc(inline)] +pub use diesel_derives::sql_function_proc as sql_function; + +#[macro_export] +#[doc(hidden)] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +macro_rules! no_arg_sql_function_body_except_to_sql { + ($type_name:ident, $return_type:ty, $docs:expr) => { + #[allow(non_camel_case_types)] + #[doc=$docs] + #[derive( + Debug, + Clone, + Copy, + $crate::query_builder::QueryId, + $crate::expression::ValidGrouping + )] + pub struct $type_name; + + impl $crate::expression::Expression for $type_name { + type SqlType = $return_type; + } + + impl $crate::expression::SelectableExpression for $type_name {} + + impl $crate::expression::AppearsOnTable for $type_name {} + }; +} + +#[macro_export] +#[doc(hidden)] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +macro_rules! no_arg_sql_function_body { + ($type_name:ident, $return_type:ty, $docs:expr, $($constraint:ident)::+) => { + no_arg_sql_function_body_except_to_sql!($type_name, $return_type, $docs); + + impl $crate::query_builder::QueryFragment for $type_name where + DB: $crate::backend::Backend + $($constraint)::+, + { + fn walk_ast(&self, mut out: $crate::query_builder::AstPass) -> $crate::result::QueryResult<()> { + out.push_sql(concat!(stringify!($type_name), "()")); + Ok(()) + } + } + }; + + ($type_name:ident, $return_type:ty, $docs:expr) => { + no_arg_sql_function_body_except_to_sql!($type_name, $return_type, $docs); + + impl $crate::query_builder::QueryFragment for $type_name where + DB: $crate::backend::Backend, + { + fn walk_ast(&self, mut out: $crate::query_builder::AstPass) -> $crate::result::QueryResult<()> { + out.push_sql(concat!(stringify!($type_name), "()")); + Ok(()) + } + } + }; +} + +#[macro_export] +/// Declare a 0 argument SQL function for use in your code. This will generate a +/// unit struct, which is an expression representing calling this function. See +/// [`now`](expression/dsl/struct.now.html) for example output. `now` was +/// generated using: +/// +/// ```no_run +/// # pub use diesel::*; +/// no_arg_sql_function!(now, sql_types::Timestamp, "Represents the SQL NOW() function"); +/// # fn main() {} +/// ``` +/// +/// You can optionally pass the name of a trait, as a constraint for backends which support the +/// function. +#[deprecated( + since = "2.0.0", + note = "Use `sql_function!` instead. See `CHANGELOG.md` for migration instructions" +)] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +macro_rules! no_arg_sql_function { + ($type_name:ident, $return_type:ty) => { + no_arg_sql_function!($type_name, $return_type, ""); + }; + + ($type_name:ident, $return_type:ty, $docs:expr) => { + no_arg_sql_function_body!($type_name, $return_type, $docs); + }; + + ($type_name:ident, $return_type:ty, $docs:expr, $($constraint:ident)::+) => { + no_arg_sql_function_body!($type_name, $return_type, $docs, $($constraint)::+); + }; +} + +#[doc(hidden)] +pub mod aggregate_folding; +#[doc(hidden)] +pub mod aggregate_ordering; +#[doc(hidden)] +pub mod date_and_time; +#[doc(hidden)] +pub mod helper_types; diff --git a/collector/benchmarks/diesel/diesel/src/expression/grouped.rs b/collector/benchmarks/diesel/diesel/src/expression/grouped.rs new file mode 100644 index 000000000..bd775821b --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/grouped.rs @@ -0,0 +1,23 @@ +use crate::backend::Backend; +use crate::expression::{Expression, ValidGrouping}; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types::DieselNumericOps; + +#[derive(Debug, Copy, Clone, QueryId, Default, DieselNumericOps, ValidGrouping)] +pub struct Grouped(pub T); + +impl Expression for Grouped { + type SqlType = T::SqlType; +} + +impl, DB: Backend> QueryFragment for Grouped { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("("); + self.0.walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } +} + +impl_selectable_expression!(Grouped); diff --git a/collector/benchmarks/diesel/diesel/src/expression/helper_types.rs b/collector/benchmarks/diesel/diesel/src/expression/helper_types.rs new file mode 100644 index 000000000..3b3e8e557 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/helper_types.rs @@ -0,0 +1,120 @@ +//! The types in this module are all shorthand for `PredicateType>`. Since we often need to return concrete types, instead of +//! a boxed trait object, these can be useful for writing concise return types. +use super::array_comparison::{AsInExpression, In, NotIn}; +use super::grouped::Grouped; +use super::{AsExpression, Expression}; +use crate::sql_types; + +/// The SQL type of an expression +pub type SqlTypeOf = ::SqlType; + +/// The type of `Item` when converted to an expression with the same type as `TargetExpr` +pub type AsExpr = AsExprOf>; + +/// The type of `Item` when converted to an expression of `Type` +pub type AsExprOf = >::Expression; + +/// The return type of +/// [`lhs.eq(rhs)`](../expression_methods/trait.ExpressionMethods.html#method.eq) +pub type Eq = Grouped>>; + +/// The return type of +/// [`lhs.ne(rhs)`](../expression_methods/trait.ExpressionMethods.html#method.ne) +pub type NotEq = Grouped>>; + +/// The return type of +/// [`lhs.eq_any(rhs)`](../expression_methods/trait.ExpressionMethods.html#method.eq_any) +pub type EqAny = Grouped>>::InExpression>>; + +/// The return type of +/// [`lhs.ne_any(rhs)`](../expression_methods/trait.ExpressionMethods.html#method.ne_any) +pub type NeAny = + Grouped>>::InExpression>>; + +/// The return type of +/// [`expr.is_null()`](../expression_methods/trait.ExpressionMethods.html#method.is_null) +pub type IsNull = Grouped>; + +/// The return type of +/// [`expr.is_not_null()`](../expression_methods/trait.ExpressionMethods.html#method.is_not_null) +pub type IsNotNull = Grouped>; + +/// The return type of +/// [`lhs.gt(rhs)`](../expression_methods/trait.ExpressionMethods.html#method.gt) +pub type Gt = Grouped>>; + +/// The return type of +/// [`lhs.ge(rhs)`](../expression_methods/trait.ExpressionMethods.html#method.ge) +pub type GtEq = Grouped>>; + +/// The return type of +/// [`lhs.lt(rhs)`](../expression_methods/trait.ExpressionMethods.html#method.lt) +pub type Lt = Grouped>>; + +/// The return type of +/// [`lhs.le(rhs)`](../expression_methods/trait.ExpressionMethods.html#method.le) +pub type LtEq = Grouped>>; + +/// The return type of +/// [`lhs.between(lower, upper)`](../expression_methods/trait.ExpressionMethods.html#method.between) +pub type Between = Grouped< + super::operators::Between, AsExpr>>, +>; + +/// The return type of +/// [`lhs.not_between(lower, upper)`](../expression_methods/trait.ExpressionMethods.html#method.not_between) +pub type NotBetween = Grouped< + super::operators::NotBetween< + Lhs, + super::operators::And, AsExpr>, + >, +>; + +/// The return type of +/// [`lhs.concat(rhs)`](../expression_methods/trait.TextExpressionMethods.html#method.concat) +pub type Concat = Grouped>>; + +/// The return type of +/// [`expr.desc()`](../expression_methods/trait.ExpressionMethods.html#method.desc) +pub type Desc = super::operators::Desc; + +/// The return type of +/// [`expr.asc()`](../expression_methods/trait.ExpressionMethods.html#method.asc) +pub type Asc = super::operators::Asc; + +/// The return type of +/// [`expr.nullable()`](../expression_methods/trait.NullableExpressionMethods.html#method.nullable) +pub type Nullable = super::nullable::Nullable; + +/// The return type of +/// [`lhs.and(rhs)`](../expression_methods/trait.BoolExpressionMethods.html#method.and) +pub type And = Grouped, AsExpr>>>; + +/// The return type of +/// [`lhs.or(rhs)`](../expression_methods/trait.BoolExpressionMethods.html#method.or) +pub type Or = Grouped>>>; + +/// The return type of +/// [`lhs.escape('x')`](../expression_methods/trait.EscapeExpressionMethods.html#method.escape) +pub type Escape = Grouped< + super::operators::Escape< + ::TextExpression, + AsExprOf, + >, +>; + +/// The return type of +/// [`lhs.like(rhs)`](../expression_methods/trait.TextExpressionMethods.html#method.like) +pub type Like = Grouped>>>; + +/// The return type of +/// [`lhs.not_like(rhs)`](../expression_methods/trait.TextExpressionMethods.html#method.not_like) +pub type NotLike = Grouped>>>; + +#[doc(inline)] +pub use super::functions::helper_types::*; + +#[doc(inline)] +#[cfg(feature = "postgres")] +pub use crate::pg::expression::helper_types::*; diff --git a/collector/benchmarks/diesel/diesel/src/expression/mod.rs b/collector/benchmarks/diesel/diesel/src/expression/mod.rs new file mode 100644 index 000000000..87caafb92 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/mod.rs @@ -0,0 +1,763 @@ +//! AST types representing various typed SQL expressions. +//! +//! Almost all types implement either [`Expression`](trait.Expression.html) or +//! [`AsExpression`](trait.AsExpression.html). +//! +//! The most common expression to work with is a +//! [`Column`](../query_source/trait.Column.html). There are various methods +//! that you can call on these, found in +//! [`expression_methods`](../expression_methods). +//! +//! You can also use numeric operators such as `+` on expressions of the +//! appropriate type. +//! +//! Any primitive which implements [`ToSql`](../serialize/trait.ToSql.html) will +//! also implement [`AsExpression`](trait.AsExpression.html), allowing it to be +//! used as an argument to any of the methods described here. +#[macro_use] +#[doc(hidden)] +pub mod ops; +pub mod functions; + +#[doc(hidden)] +pub mod array_comparison; +#[doc(hidden)] +pub mod bound; +#[doc(hidden)] +pub mod coerce; +#[doc(hidden)] +pub mod count; +#[doc(hidden)] +pub mod exists; +#[doc(hidden)] +pub mod grouped; +#[doc(hidden)] +pub mod helper_types; +mod not; +#[doc(hidden)] +pub mod nullable; +#[doc(hidden)] +#[macro_use] +pub mod operators; +#[doc(hidden)] +pub mod sql_literal; +#[doc(hidden)] +pub mod subselect; + +#[doc(hidden)] +#[allow(non_camel_case_types)] +pub mod dsl { + use crate::dsl::SqlTypeOf; + + #[doc(inline)] + pub use super::count::*; + #[doc(inline)] + pub use super::exists::exists; + #[doc(inline)] + pub use super::functions::aggregate_folding::*; + #[doc(inline)] + pub use super::functions::aggregate_ordering::*; + #[doc(inline)] + pub use super::functions::date_and_time::*; + #[doc(inline)] + pub use super::not::not; + #[doc(inline)] + pub use super::sql_literal::sql; + + #[cfg(feature = "postgres")] + pub use crate::pg::expression::dsl::*; + + /// The return type of [`count(expr)`](../dsl/fn.count.html) + pub type count = super::count::count::HelperType, Expr>; + + /// The return type of [`count_star()`](../dsl/fn.count_star.html) + pub type count_star = super::count::CountStar; + + /// The return type of [`date(expr)`](../dsl/fn.date.html) + pub type date = super::functions::date_and_time::date::HelperType; +} + +#[doc(inline)] +pub use self::sql_literal::{SqlLiteral, UncheckedBind}; + +use crate::backend::Backend; +use crate::dsl::AsExprOf; +use crate::sql_types::{HasSqlType, IntoNotNullable, SingleValue, SqlType}; + +/// Represents a typed fragment of SQL. +/// +/// Apps should not need to implement this type directly, but it may be common +/// to use this in where clauses. Libraries should consider using +/// [`infix_operator!`](../macro.infix_operator.html) or +/// [`postfix_operator!`](../macro.postfix_operator.html) instead of +/// implementing this directly. +pub trait Expression { + /// The type that this expression represents in SQL + type SqlType: TypedExpressionType; +} + +/// Marker trait for possible types of [`Expression::SqlType`] +/// +/// [`Expression::SqlType`]: trait.Expression.html#associatedtype.SqlType +pub trait TypedExpressionType {} + +/// Possible types for []`Expression::SqlType`] +/// +/// [`Expression::SqlType`]: trait.Expression.html#associatedtype.SqlType +pub mod expression_types { + use super::{QueryMetadata, TypedExpressionType}; + use crate::backend::Backend; + use crate::sql_types::SingleValue; + + /// Query nodes with this expression type do not have a statically at compile + /// time known expression type. + /// + /// An example for such a query node in diesel itself, is `sql_query` as + /// we do not know which fields are returned from such a query at compile time. + /// + /// For loading values from queries returning a type of this expression, consider + /// using [`#[derive(QueryableByName)]`] on the corresponding result type. + /// + /// [`#[derive(QueryableByName)]`]: ../deserialize/derive.QueryableByName.html + #[derive(Clone, Copy, Debug)] + pub struct Untyped; + + /// Query nodes witch cannot be part of a select clause. + /// + /// If you see an error message containing `FromSqlRow` and this type + /// recheck that you have written a valid select clause + #[derive(Debug, Clone, Copy)] + pub struct NotSelectable; + + impl TypedExpressionType for Untyped {} + impl TypedExpressionType for NotSelectable {} + + impl TypedExpressionType for ST where ST: SingleValue {} + + impl QueryMetadata for DB { + fn row_metadata(_: &DB::MetadataLookup, row: &mut Vec>) { + row.push(None) + } + } +} + +impl Expression for Box { + type SqlType = T::SqlType; +} + +impl<'a, T: Expression + ?Sized> Expression for &'a T { + type SqlType = T::SqlType; +} + +/// A helper to translate type level sql type information into +/// runtime type information for specific queries +/// +/// If you do not implement a custom backend implementation +/// this trait is likely not relevant for you. +pub trait QueryMetadata: Backend { + /// The exact return value of this function is considerded to be a + /// backend specific implementation detail. You should not rely on those + /// values if you not own the corresponding backend + fn row_metadata(lookup: &Self::MetadataLookup, out: &mut Vec>); +} + +impl QueryMetadata for DB +where + DB: Backend + HasSqlType, + T: SingleValue, +{ + fn row_metadata(lookup: &Self::MetadataLookup, out: &mut Vec>) { + out.push(Some(>::metadata(lookup))) + } +} + +/// Converts a type to its representation for use in Diesel's query builder. +/// +/// This trait is used directly. Apps should typically use [`IntoSql`] instead. +/// +/// Implementations of this trait will generally do one of 3 things: +/// +/// - Return `self` for types which are already parts of Diesel's query builder +/// - Perform some implicit coercion (for example, allowing [`now`] to be used as +/// both [`Timestamp`] and [`Timestamptz`]. +/// - Indicate that the type has data which will be sent separately from the +/// query. This is generally referred as a "bind parameter". Types which +/// implement [`ToSql`] will generally implement `AsExpression` this way. +/// +/// [`IntoSql`]: trait.IntoSql.html +/// [`now`]: ../dsl/struct.now.html +/// [`Timestamp`]: ../sql_types/struct.Timestamp.html +/// [`Timestamptz`]: ../pg/types/sql_types/struct.Timestamptz.html +/// [`ToSql`]: ../serialize/trait.ToSql.html +/// +/// This trait could be [derived](derive.AsExpression.html) + +pub trait AsExpression +where + T: SqlType + TypedExpressionType, +{ + /// The expression being returned + type Expression: Expression; + + /// Perform the conversion + fn as_expression(self) -> Self::Expression; +} + +#[doc(inline)] +pub use diesel_derives::AsExpression; + +impl AsExpression for T +where + ST: SqlType + IntoNotNullable + TypedExpressionType, + ST::NotNullable: SingleValue, + self::as_expression_impl::ExpressionImplHelper::IsNull>: + self::as_expression_impl::AsExpressionHelper, + T: Expression, + T::SqlType: SqlType, +{ + type Expression = ::IsNull, + > as self::as_expression_impl::AsExpressionHelper>::Expression; + + fn as_expression(self) -> Self::Expression { + use self::as_expression_impl::AsExpressionHelper; + + let t = self::as_expression_impl::ExpressionImplHelper::< + _, + ST::IsNull, + ::IsNull, + >(self, std::marker::PhantomData); + t.as_expression() + } +} + +mod as_expression_impl { + use super::*; + use crate::sql_types::is_nullable; + + #[allow(missing_debug_implementations)] + pub struct ExpressionImplHelper( + pub T, + pub std::marker::PhantomData<(IsNullExpr, IsNullAsExpr)>, + ); + + // We could use `AsExpression` here instead of defining a new trait in theory + // in practice we hit https://github.com/rust-lang/rust/issues/77446 then + // when defining a custom type in a third party crate + pub trait AsExpressionHelper { + type Expression: Expression; + + fn as_expression(self) -> Self::Expression; + } + + // This impl is for accepting a not nullable expression in a position where + // a not nullable expression is expected + impl AsExpressionHelper + for ExpressionImplHelper + where + ST: SqlType + TypedExpressionType, + T: Expression, + { + type Expression = T; + + fn as_expression(self) -> Self::Expression { + self.0 + } + } + + // This impl is for accepting a not nullable expression in a position where + // a nullable expression is expected + impl AsExpressionHelper + for ExpressionImplHelper + where + ST: SqlType + IntoNotNullable + TypedExpressionType, + ST::NotNullable: TypedExpressionType + SqlType, + T: Expression, + super::nullable::Nullable: Expression, + { + type Expression = super::nullable::Nullable; + + fn as_expression(self) -> Self::Expression { + super::nullable::Nullable::new(self.0) + } + } + + // This impl is for accepting a nullable expression in a position where + // a nullable expression is expected + impl AsExpressionHelper + for ExpressionImplHelper + where + ST: SqlType + TypedExpressionType, + T: Expression, + { + type Expression = T; + + fn as_expression(self) -> Self::Expression { + self.0 + } + } + + // impl AsExpressionHelper for + // ExpressionImplHelper + // is missing because we don't want to accept a nullable expression in possition where + // where a not nullable expression is expected +} + +/// Converts a type to its representation for use in Diesel's query builder. +/// +/// This trait only exists to make usage of `AsExpression` more ergonomic when +/// the `SqlType` cannot be inferred. It is generally used when you need to use +/// a Rust value as the left hand side of an expression, or when you want to +/// select a constant value. +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use schema::users; +/// # +/// # fn main() { +/// use diesel::sql_types::Text; +/// # let conn = establish_connection(); +/// let names = users::table +/// .select("The Amazing ".into_sql::().concat(users::name)) +/// .load(&conn); +/// let expected_names = vec![ +/// "The Amazing Sean".to_string(), +/// "The Amazing Tess".to_string(), +/// ]; +/// assert_eq!(Ok(expected_names), names); +/// # } +/// ``` +pub trait IntoSql { + /// Convert `self` to an expression for Diesel's query builder. + /// + /// There is no difference in behavior between `x.into_sql::()` and + /// `AsExpression::::as_expression(x)`. + fn into_sql(self) -> AsExprOf + where + Self: AsExpression + Sized, + T: SqlType + TypedExpressionType, + { + self.as_expression() + } + + /// Convert `&self` to an expression for Diesel's query builder. + /// + /// There is no difference in behavior between `x.as_sql::()` and + /// `AsExpression::::as_expression(&x)`. + fn as_sql<'a, T>(&'a self) -> AsExprOf<&'a Self, T> + where + &'a Self: AsExpression, + T: SqlType + TypedExpressionType, + { + <&'a Self as AsExpression>::as_expression(self) + } +} + +impl IntoSql for T {} + +/// Indicates that all elements of an expression are valid given a from clause. +/// +/// This is used to ensure that `users.filter(posts::id.eq(1))` fails to +/// compile. This constraint is only used in places where the nullability of a +/// SQL type doesn't matter (everything except `select` and `returning`). For +/// places where nullability is important, `SelectableExpression` is used +/// instead. +pub trait AppearsOnTable: Expression {} + +impl AppearsOnTable for Box +where + T: AppearsOnTable, + Box: Expression, +{ +} + +impl<'a, T: ?Sized, QS> AppearsOnTable for &'a T +where + T: AppearsOnTable, + &'a T: Expression, +{ +} + +/// Indicates that an expression can be selected from a source. +/// +/// Columns will implement this for their table. Certain special types, like +/// `CountStar` and `Bound` will implement this for all sources. Most compound +/// expressions will implement this if each of their parts implement it. +/// +/// Notably, columns will not implement this trait for the right side of a left +/// join. To select a column or expression using a column from the right side of +/// a left join, you must call `.nullable()` on it. +pub trait SelectableExpression: AppearsOnTable {} + +impl SelectableExpression for Box +where + T: SelectableExpression, + Box: AppearsOnTable, +{ +} + +impl<'a, T: ?Sized, QS> SelectableExpression for &'a T +where + T: SelectableExpression, + &'a T: AppearsOnTable, +{ +} + +/// Is this expression valid for a given group by clause? +/// +/// Implementations of this trait must ensure that aggregate expressions are +/// not mixed with non-aggregate expressions. +/// +/// For generic types, you can determine if your sub-expresssions can appear +/// together using the [`MixedAggregates`] trait. +/// +/// `GroupByClause` will be a tuple containing the set of expressions appearing +/// in the `GROUP BY` portion of the query. If there is no `GROUP BY`, it will +/// be `()`. +/// +/// This trait can be [derived] +/// +/// [derived]: derive.ValidGrouping.html +/// [`MixedAggregates`]: trait.MixedAggregates.html +pub trait ValidGrouping { + /// Is this expression aggregate? + /// + /// This type should always be one of the structs in the [`is_aggregate`] + /// module. See the documentation of those structs for more details. + /// + /// [`is_aggregate`]: is_aggregate/index.html + type IsAggregate; +} + +impl + ?Sized, GB> ValidGrouping for Box { + type IsAggregate = T::IsAggregate; +} + +impl<'a, T: ValidGrouping + ?Sized, GB> ValidGrouping for &'a T { + type IsAggregate = T::IsAggregate; +} + +#[doc(inline)] +pub use diesel_derives::ValidGrouping; + +#[doc(hidden)] +pub trait IsContainedInGroupBy { + type Output; +} + +#[doc(hidden)] +#[allow(missing_debug_implementations, missing_copy_implementations)] +pub mod is_contained_in_group_by { + pub struct Yes; + pub struct No; + + pub trait IsAny { + type Output; + } + + impl IsAny for Yes { + type Output = Yes; + } + + impl IsAny for No { + type Output = Yes; + } + + impl IsAny for No { + type Output = No; + } +} + +/// Can two `IsAggregate` types appear in the same expression? +/// +/// You should never implement this trait. It will eventually become a trait +/// alias. +/// +/// [`is_aggregate::Yes`] and [`is_aggregate::No`] can only appear with +/// themselves or [`is_aggregate::Never`]. [`is_aggregate::Never`] can appear +/// with anything. +/// +/// [`is_aggregate::Yes`]: is_aggregate/struct.Yes.html +/// [`is_aggregate::No`]: is_aggregate/struct.No.html +/// [`is_aggregate::Never`]: is_aggregate/struct.Never.html +pub trait MixedAggregates { + /// What is the resulting `IsAggregate` type? + type Output; +} + +#[allow(missing_debug_implementations, missing_copy_implementations)] +/// Possible values for `ValidGrouping::IsAggregate` +pub mod is_aggregate { + use super::MixedAggregates; + + /// Yes, this expression is aggregate for the given group by clause. + pub struct Yes; + + /// No, this expression is not aggregate with the given group by clause, + /// but it might be aggregate with a different group by clause. + pub struct No; + + /// This expression is never aggregate, and can appear with any other + /// expression, regardless of whether it is aggregate. + /// + /// Examples of this are literals. `1` does not care about aggregation. + /// `foo + 1` is always valid, regardless of whether `foo` appears in the + /// group by clause or not. + pub struct Never; + + impl MixedAggregates for Yes { + type Output = Yes; + } + + impl MixedAggregates for Yes { + type Output = Yes; + } + + impl MixedAggregates for No { + type Output = No; + } + + impl MixedAggregates for No { + type Output = No; + } + + impl MixedAggregates for Never { + type Output = T; + } +} + +// Note that these docs are similar to but slightly different than the stable +// docs below. Make sure if you change these that you also change the docs +// below. +/// Trait alias to represent an expression that isn't aggregate by default. +/// +/// This alias represents a type which is not aggregate if there is no group by +/// clause. More specifically, it represents for types which implement +/// [`ValidGrouping<()>`] where `IsAggregate` is [`is_aggregate::No`] or +/// [`is_aggregate::Yes`]. +/// +/// While this trait is a useful stand-in for common cases, `T: NonAggregate` +/// cannot always be used when `T: ValidGrouping<(), IsAggregate = No>` or +/// `T: ValidGrouping<(), IsAggregate = Never>` could be. For that reason, +/// unless you need to abstract over both columns and literals, you should +/// prefer to use [`ValidGrouping<()>`] in your bounds instead. +/// +/// [`ValidGrouping<()>`]: trait.ValidGrouping.html +/// [`is_aggregate::Yes`]: is_aggregate/struct.Yes.html +/// [`is_aggregate::No`]: is_aggregate/struct.No.html +#[cfg(feature = "unstable")] +pub trait NonAggregate = ValidGrouping<()> +where + >::IsAggregate: + MixedAggregates; + +// Note that these docs are similar to but slightly different than the unstable +// docs above. Make sure if you change these that you also change the docs +// above. +/// Trait alias to represent an expression that isn't aggregate by default. +/// +/// This trait should never be implemented directly. It is replaced with a +/// trait alias when the `unstable` feature is enabled. +/// +/// This alias represents a type which is not aggregate if there is no group by +/// clause. More specifically, it represents for types which implement +/// [`ValidGrouping<()>`] where `IsAggregate` is [`is_aggregate::No`] or +/// [`is_aggregate::Yes`]. +/// +/// While this trait is a useful stand-in for common cases, `T: NonAggregate` +/// cannot always be used when `T: ValidGrouping<(), IsAggregate = No>` or +/// `T: ValidGrouping<(), IsAggregate = Never>` could be. For that reason, +/// unless you need to abstract over both columns and literals, you should +/// prefer to use [`ValidGrouping<()>`] in your bounds instead. +/// +/// [`ValidGrouping<()>`]: trait.ValidGrouping.html +/// [`is_aggregate::Yes`]: is_aggregate/struct.Yes.html +/// [`is_aggregate::No`]: is_aggregate/struct.No.html +#[cfg(not(feature = "unstable"))] +pub trait NonAggregate: ValidGrouping<()> {} + +#[cfg(not(feature = "unstable"))] +impl NonAggregate for T +where + T: ValidGrouping<()>, + T::IsAggregate: MixedAggregates, +{ +} + +use crate::query_builder::{QueryFragment, QueryId}; + +/// Helper trait used when boxing expressions. +/// +/// In Rust you cannot create a trait object with more than one trait. +/// This type has all of the additional traits you would want when using +/// `Box` as a single trait object. +/// +/// By default `BoxableExpression` is not usable in queries that have a custom +/// group by clause. Setting the generic parameters `GB` and `IsAggregate` allows +/// to configure the expression to be used with a specific group by clause. +/// +/// This is typically used as the return type of a function. +/// For cases where you want to dynamically construct a query, +/// [boxing the query] is usually more ergonomic. +/// +/// [boxing the query]: ../query_dsl/trait.QueryDsl.html#method.into_boxed +/// +/// # Examples +/// +/// ## Usage without group by clause +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use schema::users; +/// use diesel::sql_types::Bool; +/// +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let conn = establish_connection(); +/// enum Search { +/// Id(i32), +/// Name(String), +/// } +/// +/// # /* +/// type DB = diesel::sqlite::Sqlite; +/// # */ +/// +/// fn find_user(search: Search) -> Box> { +/// match search { +/// Search::Id(id) => Box::new(users::id.eq(id)), +/// Search::Name(name) => Box::new(users::name.eq(name)), +/// } +/// } +/// +/// let user_one = users::table +/// .filter(find_user(Search::Id(1))) +/// .first(&conn)?; +/// assert_eq!((1, String::from("Sean")), user_one); +/// +/// let tess = users::table +/// .filter(find_user(Search::Name("Tess".into()))) +/// .first(&conn)?; +/// assert_eq!((2, String::from("Tess")), tess); +/// # Ok(()) +/// # } +/// ``` +/// +/// ## Allow usage with group by clause +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// +/// # use schema::users; +/// use diesel::sql_types::Text; +/// use diesel::dsl; +/// use diesel::expression::ValidGrouping; +/// +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let conn = establish_connection(); +/// enum NameOrConst { +/// Name, +/// Const(String), +/// } +/// +/// # /* +/// type DB = diesel::sqlite::Sqlite; +/// # */ +/// +/// fn selection( +/// selection: NameOrConst +/// ) -> Box< +/// dyn BoxableExpression< +/// users::table, +/// DB, +/// GB, +/// >::IsAggregate, +/// SqlType = Text +/// > +/// > +/// where +/// users::name: BoxableExpression< +/// users::table, +/// DB, +/// GB, +/// >::IsAggregate, +/// SqlType = Text +/// > + ValidGrouping, +/// { +/// match selection { +/// NameOrConst::Name => Box::new(users::name), +/// NameOrConst::Const(name) => Box::new(name.into_sql::()), +/// } +/// } +/// +/// let user_one = users::table +/// .select(selection(NameOrConst::Name)) +/// .first::(&conn)?; +/// assert_eq!(String::from("Sean"), user_one); +/// +/// let with_name = users::table +/// .group_by(users::name) +/// .select(selection(NameOrConst::Const("Jane Doe".into()))) +/// .first::(&conn)?; +/// assert_eq!(String::from("Jane Doe"), with_name); +/// # Ok(()) +/// # } +/// ``` +pub trait BoxableExpression +where + DB: Backend, + Self: Expression, + Self: SelectableExpression, + Self: QueryFragment, +{ +} + +impl BoxableExpression for T +where + DB: Backend, + T: Expression, + T: SelectableExpression, + T: ValidGrouping, + T: QueryFragment, + T::IsAggregate: MixedAggregates, +{ +} + +impl<'a, QS, ST, DB, GB, IsAggregate> QueryId + for dyn BoxableExpression + 'a +{ + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl<'a, QS, ST, DB, GB, IsAggregate> ValidGrouping + for dyn BoxableExpression + 'a +{ + type IsAggregate = IsAggregate; +} + +/// Converts a tuple of values into a tuple of Diesel expressions. +/// +/// This trait is similar to [`AsExpression`], but it operates on tuples. +/// The expressions must all be of the same SQL type. +/// +/// [`AsExpression`]: trait.AsExpression.html +pub trait AsExpressionList { + /// The final output expression + type Expression; + + /// Perform the conversion + fn as_expression_list(self) -> Self::Expression; +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/not.rs b/collector/benchmarks/diesel/diesel/src/expression/not.rs new file mode 100644 index 000000000..96b7916c3 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/not.rs @@ -0,0 +1,31 @@ +use crate::expression::grouped::Grouped; +use crate::expression::AsExpression; +use crate::helper_types::not; +use crate::sql_types::{Bool, Nullable}; + +/// Creates a SQL `NOT` expression +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// # let connection = establish_connection(); +/// use diesel::dsl::not; +/// +/// let users_with_name = users.select(id).filter(name.eq("Sean")); +/// let users_not_with_name = users.select(id).filter( +/// not(name.eq("Sean"))); +/// +/// assert_eq!(Ok(1), users_with_name.first(&connection)); +/// assert_eq!(Ok(2), users_not_with_name.first(&connection)); +/// # } +/// ``` +pub fn not(expr: T) -> not +where + T: AsExpression>, +{ + super::operators::Not::new(Grouped(expr.as_expression())) +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/nullable.rs b/collector/benchmarks/diesel/diesel/src/expression/nullable.rs new file mode 100644 index 000000000..ee4b23481 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/nullable.rs @@ -0,0 +1,60 @@ +use crate::backend::Backend; +use crate::expression::TypedExpressionType; +use crate::expression::*; +use crate::query_builder::*; +use crate::query_source::joins::ToInnerJoin; +use crate::result::QueryResult; +use crate::sql_types::{DieselNumericOps, IntoNullable}; + +#[derive(Debug, Copy, Clone, DieselNumericOps, ValidGrouping)] +pub struct Nullable(T); + +impl Nullable { + pub fn new(expr: T) -> Self { + Nullable(expr) + } +} + +impl Expression for Nullable +where + T: Expression, + T::SqlType: IntoNullable, + ::Nullable: TypedExpressionType, +{ + type SqlType = ::Nullable; +} + +impl QueryFragment for Nullable +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + self.0.walk_ast(pass) + } +} + +/// Nullable can be used in where clauses everywhere, but can only be used in +/// select clauses for outer joins. +impl AppearsOnTable for Nullable +where + T: AppearsOnTable, + Nullable: Expression, +{ +} + +impl QueryId for Nullable { + type QueryId = T::QueryId; + + const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID; +} + +impl SelectableExpression for Nullable +where + Self: AppearsOnTable, + QS: ToInnerJoin, + T: SelectableExpression, +{ +} + +impl SelectableExpression<()> for Nullable where Self: AppearsOnTable<()> {} diff --git a/collector/benchmarks/diesel/diesel/src/expression/operators.rs b/collector/benchmarks/diesel/diesel/src/expression/operators.rs new file mode 100644 index 000000000..e68269be0 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/operators.rs @@ -0,0 +1,586 @@ +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_operator_body { + ( + notation = $notation:ident, + struct_name = $name:ident, + operator = $operator:expr, + return_ty = (ReturnBasedOnArgs), + ty_params = ($($ty_param:ident,)+), + field_names = $field_names:tt, + backend_ty_params = $backend_ty_params:tt, + backend_ty = $backend_ty:ty, + ) => { + $crate::__diesel_operator_body! { + notation = $notation, + struct_name = $name, + operator = $operator, + return_ty = (ST), + ty_params = ($($ty_param,)+), + field_names = $field_names, + backend_ty_params = $backend_ty_params, + backend_ty = $backend_ty, + expression_ty_params = (ST,), + expression_bounds = ($($ty_param: $crate::expression::Expression,)+), + } + }; + + ( + notation = $notation:ident, + struct_name = $name:ident, + operator = $operator:expr, + return_ty = ($($return_ty:tt)+), + ty_params = ($($ty_param:ident,)+), + field_names = $field_names:tt, + backend_ty_params = $backend_ty_params:tt, + backend_ty = $backend_ty:ty, + ) => { + $crate::__diesel_operator_body! { + notation = $notation, + struct_name = $name, + operator = $operator, + return_ty = ($($return_ty)*), + ty_params = ($($ty_param,)+), + field_names = $field_names, + backend_ty_params = $backend_ty_params, + backend_ty = $backend_ty, + expression_ty_params = (), + expression_bounds = ($($ty_param: $crate::expression::Expression,)+), + } + }; + + ( + notation = $notation:ident, + struct_name = $name:ident, + operator = $operator:expr, + return_ty = ($($return_ty:tt)+), + ty_params = ($($ty_param:ident,)+), + field_names = ($($field_name:ident,)+), + backend_ty_params = ($($backend_ty_param:ident,)*), + backend_ty = $backend_ty:ty, + expression_ty_params = ($($expression_ty_params:ident,)*), + expression_bounds = ($($expression_bounds:tt)*), + ) => { + #[derive( + Debug, + Clone, + Copy, + $crate::query_builder::QueryId, + $crate::sql_types::DieselNumericOps, + $crate::expression::ValidGrouping + )] + #[doc(hidden)] + pub struct $name<$($ty_param,)+> { + $(pub(crate) $field_name: $ty_param,)+ + } + + impl<$($ty_param,)+> $name<$($ty_param,)+> { + pub fn new($($field_name: $ty_param,)+) -> Self { + $name { $($field_name,)+ } + } + } + + $crate::impl_selectable_expression!($name<$($ty_param),+>); + + impl<$($ty_param,)+ $($expression_ty_params,)*> $crate::expression::Expression for $name<$($ty_param,)+> where + $($expression_bounds)* + { + type SqlType = $($return_ty)*; + } + + impl<$($ty_param,)+ $($backend_ty_param,)*> $crate::query_builder::QueryFragment<$backend_ty> + for $name<$($ty_param,)+> where + $($ty_param: $crate::query_builder::QueryFragment<$backend_ty>,)+ + $($backend_ty_param: $crate::backend::Backend,)* + { + fn walk_ast(&self, mut out: $crate::query_builder::AstPass<$backend_ty>) -> $crate::result::QueryResult<()> { + $crate::__diesel_operator_to_sql!( + notation = $notation, + operator_expr = out.push_sql($operator), + field_exprs = ($(self.$field_name.walk_ast(out.reborrow())?),+), + ); + Ok(()) + } + } + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_operator_to_sql { + ( + notation = infix, + operator_expr = $op:expr, + field_exprs = ($left:expr, $right:expr), + ) => { + $left; + $op; + $right; + }; + + ( + notation = postfix, + operator_expr = $op:expr, + field_exprs = ($expr:expr), + ) => { + $expr; + $op; + }; + + ( + notation = prefix, + operator_expr = $op:expr, + field_exprs = ($expr:expr), + ) => { + $op; + $expr; + }; +} + +/// Useful for libraries adding support for new SQL types. Apps should never +/// need to call this. +/// +/// This will create a new type with the given name. It will implement all +/// methods needed to be used as an expression in Diesel, placing the given +/// SQL between the two elements. The third argument specifies the SQL type +/// that the operator returns. If it is not given, the type will be assumed +/// to be `Bool`. +/// +/// If the operator is specific to a single backend, you can specify this by +/// adding `backend: Pg` or similar as the last argument. +/// +/// It should be noted that the generated impls will not constrain the SQL +/// types of the arguments. You should ensure that they are of the right +/// type in your function which constructs the operator. +/// +/// Typically you would not expose the type that this generates directly. You'd +/// expose a function (or trait) used to construct the expression, and a helper +/// type which represents the return type of that function. See the source of +/// `diesel::expression::expression_methods` and +/// `diesel::expression::helper_types` for real world examples of this. +/// +/// # Examples +/// +/// # Possible invocations +/// +/// ```ignore +/// // The SQL type will be boolean. The backend will not be constrained +/// infix_operator!(Matches, " @@ "); +/// +/// // Queries which try to execute `Contains` on a backend other than Pg +/// // will fail to compile +/// infix_operator!(Contains, " @> ", backend: Pg); +/// +/// // The type of `Concat` will be `TsVector` rather than Bool +/// infix_operator!(Concat, " || ", TsVector); +/// +/// // It is perfectly fine to have multiple operators with the same SQL. +/// // Diesel will ensure that the queries are always unambiguous in which +/// // operator applies +/// infix_operator!(Or, " || ", TsQuery); +/// +/// // Specifying both the return types and the backend +/// infix_operator!(And, " && ", TsQuery, backend: Pg); +/// ``` +/// +/// ## Example usage +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use diesel::sql_types::SqlType; +/// # use diesel::expression::TypedExpressionType; +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// # let connection = establish_connection(); +/// diesel::infix_operator!(MyEq, " = "); +/// +/// use diesel::expression::AsExpression; +/// +/// // Normally you would put this on a trait instead +/// fn my_eq(left: T, right: U) -> MyEq where +/// T: Expression, +/// U: AsExpression, +/// ST: SqlType + TypedExpressionType, +/// { +/// MyEq::new(left, right.as_expression()) +/// } +/// +/// let users_with_name = users.select(id).filter(my_eq(name, "Sean")); +/// +/// assert_eq!(Ok(1), users_with_name.first(&connection)); +/// # } +/// ``` +#[macro_export] +macro_rules! infix_operator { + ($name:ident, $operator:expr) => { + $crate::infix_operator!($name, $operator, $crate::sql_types::Bool); + }; + + ($name:ident, $operator:expr, backend: $backend:ty) => { + $crate::infix_operator!($name, $operator, $crate::sql_types::Bool, backend: $backend); + }; + + ($name:ident, $operator:expr, $($return_ty:tt)::*) => { + $crate::__diesel_operator_body!( + notation = infix, + struct_name = $name, + operator = $operator, + return_ty = ( + $crate::sql_types::is_nullable::MaybeNullable< + $crate::sql_types::is_nullable::IsOneNullable< + ::SqlType, + ::SqlType + >, + $($return_ty)::* + > + ), + ty_params = (T, U,), + field_names = (left, right,), + backend_ty_params = (DB,), + backend_ty = DB, + expression_ty_params = (), + expression_bounds = ( + T: $crate::expression::Expression, + U: $crate::expression::Expression, + ::SqlType: $crate::sql_types::SqlType, + ::SqlType: $crate::sql_types::SqlType, + $crate::sql_types::is_nullable::IsSqlTypeNullable< + ::SqlType + >: $crate::sql_types::OneIsNullable< + $crate::sql_types::is_nullable::IsSqlTypeNullable< + ::SqlType + > + >, + $crate::sql_types::is_nullable::IsOneNullable< + ::SqlType, + ::SqlType + >: $crate::sql_types::MaybeNullableType<$($return_ty)::*>, + ), + ); + }; + + ($name:ident, $operator:expr, $return_ty:ty, backend: $backend:ty) => { + $crate::__diesel_operator_body!( + notation = infix, + struct_name = $name, + operator = $operator, + return_ty = ( + $crate::sql_types::is_nullable::MaybeNullable< + $crate::sql_types::is_nullable::IsOneNullable< + ::SqlType, + ::SqlType + >, + $return_ty, + > + ), + ty_params = (T, U,), + field_names = (left, right,), + backend_ty_params = (), + backend_ty = $backend, + expression_ty_params = (), + expression_bounds = ( + T: $crate::expression::Expression, + U: $crate::expression::Expression, + ::SqlType: $crate::sql_types::SqlType, + ::SqlType: $crate::sql_types::SqlType, + $crate::sql_types::is_nullable::IsSqlTypeNullable< + ::SqlType + >: $crate::sql_types::OneIsNullable< + $crate::sql_types::is_nullable::IsSqlTypeNullable< + ::SqlType + > + >, + $crate::sql_types::is_nullable::IsOneNullable< + ::SqlType, + ::SqlType + >: $crate::sql_types::MaybeNullableType<$return_ty>, + ), + ); + }; +} + +#[macro_export] +#[deprecated(since = "2.0.0", note = "use `diesel::infix_operator!` instead")] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +#[doc(hidden)] +macro_rules! diesel_infix_operator { + ($($args:tt)*) => { + $crate::infix_operator!($($args)*); + } +} + +/// Useful for libraries adding support for new SQL types. Apps should never +/// need to call this. +/// +/// Similar to [`infix_operator!`], but the generated type will only take +/// a single argument rather than two. The operator SQL will be placed after +/// the single argument. See [`infix_operator!`] for example usage. +/// +/// [`infix_operator!`]: macro.infix_operator.html +#[macro_export] +macro_rules! postfix_operator { + ($name:ident, $operator:expr) => { + $crate::postfix_operator!($name, $operator, $crate::sql_types::Bool); + }; + + ($name:ident, $operator:expr, backend: $backend:ty) => { + $crate::postfix_operator!($name, $operator, $crate::sql_types::Bool, backend: $backend); + }; + + ($name:ident, $operator:expr, $return_ty:ty) => { + $crate::__diesel_operator_body!( + notation = postfix, + struct_name = $name, + operator = $operator, + return_ty = ($return_ty), + ty_params = (Expr,), + field_names = (expr,), + backend_ty_params = (DB,), + backend_ty = DB, + ); + }; + + ($name:ident, $operator:expr, $return_ty:ty, backend: $backend:ty) => { + $crate::__diesel_operator_body!( + notation = postfix, + struct_name = $name, + operator = $operator, + return_ty = ($return_ty), + ty_params = (Expr,), + field_names = (expr,), + backend_ty_params = (), + backend_ty = $backend, + ); + }; +} + +#[macro_export] +#[deprecated(since = "2.0.0", note = "use `diesel::postfix_operator!` instead")] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +#[doc(hidden)] +macro_rules! diesel_postfix_operator { + ($($args:tt)*) => { + $crate::postfix_operator!($($args)*); + } +} + +/// Useful for libraries adding support for new SQL types. Apps should never +/// need to call this. +/// +/// Similar to [`infix_operator!`], but the generated type will only take +/// a single argument rather than two. The operator SQL will be placed before +/// the single argument. See [`infix_operator!`] for example usage. +/// +/// [`infix_operator!`]: macro.infix_operator.html +#[macro_export] +macro_rules! prefix_operator { + ($name:ident, $operator:expr) => { + $crate::prefix_operator!($name, $operator, $crate::sql_types::Bool); + }; + + ($name:ident, $operator:expr, backend: $backend:ty) => { + $crate::prefix_operator!($name, $operator, $crate::sql_types::Bool, backend: $backend); + }; + + ($name:ident, $operator:expr, $return_ty:ty) => { + $crate::__diesel_operator_body!( + notation = prefix, + struct_name = $name, + operator = $operator, + return_ty = ($return_ty), + ty_params = (Expr,), + field_names = (expr,), + backend_ty_params = (DB,), + backend_ty = DB, + ); + }; + + ($name:ident, $operator:expr, $return_ty:ty, backend: $backend:ty) => { + $crate::__diesel_operator_body!( + notation = prefix, + struct_name = $name, + operator = $operator, + return_ty = ($return_ty), + ty_params = (Expr,), + field_names = (expr,), + backend_ty_params = (), + backend_ty = $backend, + ); + }; +} + +#[macro_export] +#[deprecated(since = "2.0.0", note = "use `diesel::prefix_operator!` instead")] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +#[doc(hidden)] +macro_rules! diesel_prefix_operator { + ($($args:tt)*) => { + $crate::prefix_operator!($($args)*); + } +} + +infix_operator!(And, " AND "); +infix_operator!(Escape, " ESCAPE "); +infix_operator!(Eq, " = "); +infix_operator!(Gt, " > "); +infix_operator!(GtEq, " >= "); +infix_operator!(Like, " LIKE "); +infix_operator!(Lt, " < "); +infix_operator!(LtEq, " <= "); +infix_operator!(NotEq, " != "); +infix_operator!(NotLike, " NOT LIKE "); +infix_operator!(Between, " BETWEEN "); +infix_operator!(NotBetween, " NOT BETWEEN "); + +postfix_operator!(IsNull, " IS NULL"); +postfix_operator!(IsNotNull, " IS NOT NULL"); +postfix_operator!( + Asc, + " ASC ", + crate::expression::expression_types::NotSelectable +); +postfix_operator!( + Desc, + " DESC ", + crate::expression::expression_types::NotSelectable +); + +prefix_operator!( + Not, + " NOT ", + crate::sql_types::Nullable +); + +use crate::backend::Backend; +use crate::expression::{TypedExpressionType, ValidGrouping}; +use crate::insertable::{ColumnInsertValue, Insertable}; +use crate::query_builder::{QueryFragment, QueryId, ValuesClause}; +use crate::query_source::Column; +use crate::sql_types::{ + is_nullable, AllAreNullable, Bool, DieselNumericOps, MaybeNullableType, SqlType, +}; +use crate::Expression; + +impl Insertable for Eq +where + T: Column, +{ + type Values = ValuesClause, T::Table>; + + fn values(self) -> Self::Values { + ValuesClause::new(ColumnInsertValue::Expression(self.left, self.right)) + } +} + +impl<'a, T, Tab, U> Insertable for &'a Eq +where + T: Copy, + Eq: Insertable, +{ + type Values = as Insertable>::Values; + + fn values(self) -> Self::Values { + Eq::new(self.left, &self.right).values() + } +} + +#[derive(Debug, Clone, Copy, QueryId, DieselNumericOps, ValidGrouping)] +#[doc(hidden)] +pub struct Concat { + pub(crate) left: L, + pub(crate) right: R, +} + +impl Concat { + pub fn new(left: L, right: R) -> Self { + Self { left, right } + } +} + +impl crate::expression::Expression for Concat +where + L: crate::expression::Expression, + R: crate::expression::Expression, + ST: SqlType + TypedExpressionType, +{ + type SqlType = ST; +} + +impl_selectable_expression!(Concat); + +impl QueryFragment for Concat +where + L: QueryFragment, + R: QueryFragment, + DB: Backend, +{ + fn walk_ast( + &self, + mut out: crate::query_builder::AstPass, + ) -> crate::result::QueryResult<()> { + // Those brackets are required because mysql is broken + // https://github.com/diesel-rs/diesel/issues/2133#issuecomment-517432317 + out.push_sql("("); + self.left.walk_ast(out.reborrow())?; + out.push_sql(" || "); + self.right.walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } +} + +// or is different +// it only evaluates to null if both sides are null +#[derive( + Debug, + Clone, + Copy, + crate::query_builder::QueryId, + crate::sql_types::DieselNumericOps, + crate::expression::ValidGrouping, +)] +#[doc(hidden)] +pub struct Or { + pub(crate) left: T, + pub(crate) right: U, +} + +impl Or { + pub fn new(left: T, right: U) -> Self { + Or { left, right } + } +} + +impl_selectable_expression!(Or); + +impl Expression for Or +where + T: Expression, + U: Expression, + T::SqlType: SqlType, + U::SqlType: SqlType, + is_nullable::IsSqlTypeNullable: + AllAreNullable>, + is_nullable::AreAllNullable: MaybeNullableType, +{ + type SqlType = + is_nullable::MaybeNullable, Bool>; +} + +impl crate::query_builder::QueryFragment for Or +where + DB: crate::backend::Backend, + T: crate::query_builder::QueryFragment, + U: crate::query_builder::QueryFragment, +{ + fn walk_ast( + &self, + mut out: crate::query_builder::AstPass, + ) -> crate::result::QueryResult<()> { + self.left.walk_ast(out.reborrow())?; + out.push_sql(" OR "); + self.right.walk_ast(out.reborrow())?; + Ok(()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/ops/mod.rs b/collector/benchmarks/diesel/diesel/src/expression/ops/mod.rs new file mode 100644 index 000000000..580a2932d --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/ops/mod.rs @@ -0,0 +1,31 @@ +macro_rules! generic_numeric_expr_inner { + ($tpe: ident, ($($param: ident),*), $op: ident, $fn_name: ident) => { + impl ::std::ops::$op for $tpe<$($param),*> where + $tpe<$($param),*>: $crate::expression::Expression, + <$tpe<$($param),*> as $crate::Expression>::SqlType: $crate::sql_types::SqlType + $crate::sql_types::ops::$op, + <<$tpe<$($param),*> as $crate::Expression>::SqlType as $crate::sql_types::ops::$op>::Rhs: $crate::expression::TypedExpressionType, + Rhs: $crate::expression::AsExpression< + <<$tpe<$($param),*> as $crate::Expression>::SqlType as $crate::sql_types::ops::$op>::Rhs, + >, + { + type Output = $crate::expression::ops::$op; + + fn $fn_name(self, rhs: Rhs) -> Self::Output { + $crate::expression::ops::$op::new(self, rhs.as_expression()) + } + } + } +} + +macro_rules! generic_numeric_expr { + ($tpe: ident, $($param: ident),*) => { + generic_numeric_expr_inner!($tpe, ($($param),*), Add, add); + generic_numeric_expr_inner!($tpe, ($($param),*), Sub, sub); + generic_numeric_expr_inner!($tpe, ($($param),*), Div, div); + generic_numeric_expr_inner!($tpe, ($($param),*), Mul, mul); + } +} + +mod numeric; + +pub use self::numeric::{Add, Div, Mul, Sub}; diff --git a/collector/benchmarks/diesel/diesel/src/expression/ops/numeric.rs b/collector/benchmarks/diesel/diesel/src/expression/ops/numeric.rs new file mode 100644 index 000000000..1d5a5d415 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/ops/numeric.rs @@ -0,0 +1,58 @@ +use crate::backend::Backend; +use crate::expression::{Expression, TypedExpressionType, ValidGrouping}; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types; + +macro_rules! numeric_operation { + ($name:ident, $op:expr) => { + #[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] + pub struct $name { + lhs: Lhs, + rhs: Rhs, + } + + impl $name { + pub fn new(left: Lhs, right: Rhs) -> Self { + $name { + lhs: left, + rhs: right, + } + } + } + + impl Expression for $name + where + Lhs: Expression, + Lhs::SqlType: sql_types::ops::$name, + Rhs: Expression, + ::Output: TypedExpressionType, + { + type SqlType = ::Output; + } + + impl QueryFragment for $name + where + DB: Backend, + Lhs: QueryFragment, + Rhs: QueryFragment, + { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("("); + self.lhs.walk_ast(out.reborrow())?; + out.push_sql($op); + self.rhs.walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } + } + + impl_selectable_expression!($name); + generic_numeric_expr!($name, A, B); + }; +} + +numeric_operation!(Add, " + "); +numeric_operation!(Sub, " - "); +numeric_operation!(Mul, " * "); +numeric_operation!(Div, " / "); diff --git a/collector/benchmarks/diesel/diesel/src/expression/sql_literal.rs b/collector/benchmarks/diesel/diesel/src/expression/sql_literal.rs new file mode 100644 index 000000000..68c0e0abe --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/sql_literal.rs @@ -0,0 +1,348 @@ +use std::marker::PhantomData; + +use crate::backend::Backend; +use crate::expression::*; +use crate::query_builder::*; +use crate::query_dsl::RunQueryDsl; +use crate::result::QueryResult; +use crate::sql_types::{DieselNumericOps, SqlType}; + +#[derive(Debug, Clone, DieselNumericOps)] +#[must_use = "Queries are only executed when calling `load`, `get_result`, or similar."] +/// Returned by the [`sql()`] function. +/// +/// [`sql()`]: ../dsl/fn.sql.html +pub struct SqlLiteral { + sql: String, + inner: T, + _marker: PhantomData, +} + +impl SqlLiteral +where + ST: TypedExpressionType, +{ + #[doc(hidden)] + pub fn new(sql: String, inner: T) -> Self { + SqlLiteral { + sql: sql, + inner: inner, + _marker: PhantomData, + } + } + + /// Bind a value for use with this SQL query. + /// + /// # Safety + /// + /// This function should be used with care, as Diesel cannot validate that + /// the value is of the right type nor can it validate that you have passed + /// the correct number of parameters. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # } + /// # } + /// # + /// # fn main() { + /// # use self::users::dsl::*; + /// # use diesel::dsl::sql; + /// # use diesel::sql_types::{Integer, Text, Bool}; + /// # let connection = establish_connection(); + /// let seans_id = users + /// .select(id) + /// .filter(sql::("name = ").bind::("Sean")) + /// .get_result(&connection); + /// assert_eq!(Ok(1), seans_id); + /// + /// let tess_id = sql::("SELECT id FROM users WHERE name = ") + /// .bind::("Tess") + /// .get_result(&connection); + /// assert_eq!(Ok(2), tess_id); + /// # } + /// ``` + /// + /// ### Multiple Bind Params + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # } + /// # } + /// # + /// # fn main() { + /// # use self::users::dsl::*; + /// # use diesel::dsl::sql; + /// # use diesel::sql_types::{Integer, Text, Bool}; + /// # let connection = establish_connection(); + /// # diesel::insert_into(users).values(name.eq("Ryan")) + /// # .execute(&connection).unwrap(); + /// let query = users + /// .select(name) + /// .filter( + /// sql::("id > ") + /// .bind::(1) + /// .sql(" AND name <> ") + /// .bind::("Ryan") + /// ) + /// .get_results(&connection); + /// let expected = vec!["Tess".to_string()]; + /// assert_eq!(Ok(expected), query); + /// # } + /// ``` + pub fn bind(self, bind_value: U) -> UncheckedBind + where + BindST: SqlType + TypedExpressionType, + U: AsExpression, + { + UncheckedBind::new(self, bind_value.as_expression()) + } + + /// Use literal SQL in the query builder + /// + /// This function is intended for use when you need a small bit of raw SQL in + /// your query. If you want to write the entire query using raw SQL, use + /// [`sql_query`](../fn.sql_query.html) instead. + /// + /// # Safety + /// + /// This function should be used with care, as Diesel cannot validate that + /// the value is of the right type nor can it validate that you have passed + /// the correct number of parameters. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # } + /// # } + /// # + /// # fn main() { + /// # use self::users::dsl::*; + /// # use diesel::dsl::sql; + /// # use diesel::sql_types::Bool; + /// # let connection = establish_connection(); + /// # diesel::insert_into(users).values(name.eq("Ryan")) + /// # .execute(&connection).unwrap(); + /// let query = users + /// .select(name) + /// .filter( + /// sql::("id > 1") + /// .sql(" AND name <> 'Ryan'") + /// ) + /// .get_results(&connection); + /// let expected = vec!["Tess".to_string()]; + /// assert_eq!(Ok(expected), query); + /// # } + /// ``` + pub fn sql(self, sql: &str) -> SqlLiteral { + SqlLiteral::new(sql.into(), self) + } +} + +impl Expression for SqlLiteral +where + ST: TypedExpressionType, +{ + type SqlType = ST; +} + +impl QueryFragment for SqlLiteral +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + self.inner.walk_ast(out.reborrow())?; + out.push_sql(&self.sql); + Ok(()) + } +} + +impl QueryId for SqlLiteral { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl Query for SqlLiteral +where + Self: Expression, +{ + type SqlType = ST; +} + +impl RunQueryDsl for SqlLiteral {} + +impl SelectableExpression for SqlLiteral where Self: Expression {} + +impl AppearsOnTable for SqlLiteral where Self: Expression {} + +impl ValidGrouping for SqlLiteral { + type IsAggregate = is_aggregate::Never; +} + +/// Use literal SQL in the query builder +/// +/// Available for when you truly cannot represent something using the expression +/// DSL. You will need to provide the SQL type of the expression, in addition to +/// the SQL. +/// +/// This function is intended for use when you need a small bit of raw SQL in +/// your query. If you want to write the entire query using raw SQL, use +/// [`sql_query`](../fn.sql_query.html) instead. +/// +/// # Safety +/// +/// The compiler will be unable to verify the correctness of the annotated type. +/// If you give the wrong type, it'll either return an error when deserializing +/// the query result or produce unexpected values. +/// +/// # Examples +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # use diesel::sql_types::Bool; +/// use diesel::dsl::sql; +/// # let connection = establish_connection(); +/// let user = users.filter(sql::("name = 'Sean'")).first(&connection)?; +/// let expected = (1, String::from("Sean")); +/// assert_eq!(expected, user); +/// # Ok(()) +/// # } +/// ``` +pub fn sql(sql: &str) -> SqlLiteral +where + ST: TypedExpressionType, +{ + SqlLiteral::new(sql.into(), ()) +} + +#[derive(QueryId, Debug, Clone, Copy)] +#[must_use = "Queries are only executed when calling `load`, `get_result`, or similar."] +/// Returned by the [`SqlLiteral::bind()`] method when binding a value to a fragment of SQL. +/// +/// [`bind()`]: ./struct.SqlLiteral.html#method.bind +pub struct UncheckedBind { + query: Query, + value: Value, +} + +impl UncheckedBind +where + Query: Expression, +{ + pub(crate) fn new(query: Query, value: Value) -> Self { + UncheckedBind { query, value } + } + + /// Use literal SQL in the query builder + /// + /// This function is intended for use when you need a small bit of raw SQL in + /// your query. If you want to write the entire query using raw SQL, use + /// [`sql_query`](../fn.sql_query.html) instead. + /// + /// # Safety + /// + /// This function should be used with care, as Diesel cannot validate that + /// the value is of the right type nor can it validate that you have passed + /// the correct number of parameters. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # } + /// # } + /// # + /// # fn main() { + /// # use self::users::dsl::*; + /// # use diesel::dsl::sql; + /// # use diesel::sql_types::{Integer, Bool}; + /// # let connection = establish_connection(); + /// # diesel::insert_into(users).values(name.eq("Ryan")) + /// # .execute(&connection).unwrap(); + /// let query = users + /// .select(name) + /// .filter( + /// sql::("id > ") + /// .bind::(1) + /// .sql(" AND name <> 'Ryan'") + /// ) + /// .get_results(&connection); + /// let expected = vec!["Tess".to_string()]; + /// assert_eq!(Ok(expected), query); + /// # } + /// ``` + pub fn sql(self, sql: &str) -> SqlLiteral { + SqlLiteral::new(sql.into(), self) + } +} + +impl Expression for UncheckedBind +where + Query: Expression, +{ + type SqlType = Query::SqlType; +} + +impl QueryFragment for UncheckedBind +where + DB: Backend, + Query: QueryFragment, + Value: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.query.walk_ast(out.reborrow())?; + self.value.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl Query for UncheckedBind +where + Q: Query, +{ + type SqlType = Q::SqlType; +} + +impl ValidGrouping for UncheckedBind { + type IsAggregate = is_aggregate::Never; +} + +impl SelectableExpression for UncheckedBind where + Self: AppearsOnTable +{ +} + +impl AppearsOnTable for UncheckedBind where Self: Expression {} + +impl RunQueryDsl for UncheckedBind {} diff --git a/collector/benchmarks/diesel/diesel/src/expression/subselect.rs b/collector/benchmarks/diesel/diesel/src/expression/subselect.rs new file mode 100644 index 000000000..a0bab4e8b --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/subselect.rs @@ -0,0 +1,69 @@ +use std::marker::PhantomData; + +use crate::expression::array_comparison::MaybeEmpty; +use crate::expression::*; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types::SqlType; + +#[derive(Debug, Copy, Clone, QueryId)] +pub struct Subselect { + values: T, + _sql_type: PhantomData, +} + +impl Subselect { + pub(crate) fn new(values: T) -> Self { + Self { + values, + _sql_type: PhantomData, + } + } +} + +impl Expression for Subselect +where + ST: SqlType + TypedExpressionType, +{ + type SqlType = ST; +} + +impl MaybeEmpty for Subselect { + fn is_empty(&self) -> bool { + false + } +} + +impl SelectableExpression for Subselect +where + Subselect: AppearsOnTable, + T: ValidSubselect, +{ +} + +impl AppearsOnTable for Subselect +where + Subselect: Expression, + T: ValidSubselect, +{ +} + +// FIXME: This probably isn't sound. The subselect can reference columns from +// the outer query, and is affected by the `GROUP BY` clause of the outer query +// identically to using it outside of a subselect +impl ValidGrouping for Subselect { + type IsAggregate = is_aggregate::Never; +} + +impl QueryFragment for Subselect +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.values.walk_ast(out.reborrow())?; + Ok(()) + } +} + +pub trait ValidSubselect {} diff --git a/collector/benchmarks/diesel/diesel/src/expression_methods/bool_expression_methods.rs b/collector/benchmarks/diesel/diesel/src/expression_methods/bool_expression_methods.rs new file mode 100644 index 000000000..9db25db86 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression_methods/bool_expression_methods.rs @@ -0,0 +1,106 @@ +use crate::dsl; +use crate::expression::grouped::Grouped; +use crate::expression::operators::{And, Or}; +use crate::expression::{AsExpression, Expression}; +use crate::sql_types::{BoolOrNullableBool, IntoNullable, SingleValue, SqlType}; + +/// Methods present on boolean expressions +pub trait BoolExpressionMethods: Expression + Sized { + /// Creates a SQL `AND` expression + /// + /// # Example + /// + /// ``` + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// # + /// diesel::insert_into(animals) + /// .values(&vec![ + /// (species.eq("ferret"), legs.eq(4), name.eq("Freddy")), + /// (species.eq("ferret"), legs.eq(4), name.eq("Jack")), + /// ]) + /// .execute(&connection)?; + /// + /// let data = animals.select((species, name)) + /// .filter(species.eq("ferret").and(name.eq("Jack"))) + /// .load(&connection)?; + /// let expected = vec![ + /// (String::from("ferret"), Some(String::from("Jack"))), + /// ]; + /// assert_eq!(expected, data); + /// # Ok(()) + /// # } + fn and(self, other: T) -> dsl::And + where + Self::SqlType: SqlType + IntoNullable, + ::Nullable: SingleValue, + T: AsExpression<::Nullable>, + And: Expression, + { + Grouped(And::new( + crate::expression::nullable::Nullable::new(self), + other.as_expression(), + )) + } + + /// Creates a SQL `OR` expression + /// + /// The result will be wrapped in parenthesis, so that precedence matches + /// that of your function calls. For example, `false.and(false.or(true))` + /// will generate the SQL `FALSE AND (FALSE OR TRUE)`, which returns `false` + /// + /// # Example + /// + /// ``` + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// # + /// diesel::insert_into(animals) + /// .values(&vec![ + /// (species.eq("ferret"), legs.eq(4), name.eq("Freddy")), + /// (species.eq("ferret"), legs.eq(4), name.eq("Jack")), + /// ]) + /// .execute(&connection)?; + /// + /// let data = animals.select((species, name)) + /// .filter(species.eq("ferret").or(name.eq("Jack"))) + /// .load(&connection)?; + /// let expected = vec![ + /// (String::from("dog"), Some(String::from("Jack"))), + /// (String::from("ferret"), Some(String::from("Freddy"))), + /// (String::from("ferret"), Some(String::from("Jack"))), + /// ]; + /// assert_eq!(expected, data); + /// # Ok(()) + /// # } + fn or(self, other: T) -> dsl::Or + where + Self::SqlType: SqlType + IntoNullable, + ::Nullable: SingleValue, + T: AsExpression<::Nullable>, + Or: Expression, + { + Grouped(Or::new(self, other.as_expression())) + } +} + +impl BoolExpressionMethods for T +where + T: Expression, + T::SqlType: BoolOrNullableBool, +{ +} diff --git a/collector/benchmarks/diesel/diesel/src/expression_methods/eq_all.rs b/collector/benchmarks/diesel/diesel/src/expression_methods/eq_all.rs new file mode 100644 index 000000000..9efce1faa --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression_methods/eq_all.rs @@ -0,0 +1,73 @@ +use crate::expression::grouped::Grouped; +use crate::expression::nullable::Nullable; +use crate::expression::operators::And; +use crate::expression::Expression; +use crate::expression_methods::*; +use crate::sql_types::{self, Bool}; + +/// This method is used by `FindDsl` to work with tuples. Because we cannot +/// express this without specialization or overlapping impls, it is brute force +/// implemented on columns in the `column!` macro. +#[doc(hidden)] +pub trait EqAll { + type Output: Expression>; + + fn eq_all(self, rhs: Rhs) -> Self::Output; +} + +macro_rules! impl_eq_all { + // General case for 2+ elements + ( + ($Left1:ident, $($Left:ident,)+) + ($Right1:ident, $($Right:ident,)+) + ) => { + #[allow(non_snake_case)] + impl<$Left1, $($Left,)+ $Right1, $($Right,)+> + EqAll<($Right1, $($Right,)+)> for ($Left1, $($Left,)+) + where + $Left1: EqAll<$Right1>, + ($($Left,)+): EqAll<($($Right,)+)>, + { + type Output = Grouped>::Output>, + <($($Left,)+) as EqAll<($($Right,)+)>>::Output, + >>; + + fn eq_all(self, rhs: ($Right1, $($Right,)+)) -> Self::Output { + let ($Left1, $($Left,)+) = self; + let ($Right1, $($Right,)+) = rhs; + $Left1.eq_all($Right1).and(($($Left,)+).eq_all(($($Right,)+))) + } + } + }; + + // Special case for 1 element + ( + ($Left:ident,) ($Right:ident,) + ) => { + impl<$Left, $Right> EqAll<($Right,)> for ($Left,) + where + $Left: EqAll<$Right>, + { + type Output = <$Left as EqAll<$Right>>::Output; + + fn eq_all(self, rhs: ($Right,)) -> Self::Output { + self.0.eq_all(rhs.0) + } + } + }; +} + +macro_rules! impl_eq_all_for_all_tuples { + ($( + $unused1:tt { + $($unused2:tt -> $Left:ident, $Right:ident, $unused3:tt,)+ + } + )+) => { + $( + impl_eq_all!(($($Left,)+) ($($Right,)+)); + )+ + }; +} + +__diesel_for_each_tuple!(impl_eq_all_for_all_tuples); diff --git a/collector/benchmarks/diesel/diesel/src/expression_methods/escape_expression_methods.rs b/collector/benchmarks/diesel/diesel/src/expression_methods/escape_expression_methods.rs new file mode 100644 index 000000000..e7525e0e9 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression_methods/escape_expression_methods.rs @@ -0,0 +1,62 @@ +use crate::dsl; +use crate::expression::grouped::Grouped; +use crate::expression::operators::{Escape, Like, NotLike}; +use crate::expression::IntoSql; +use crate::sql_types::VarChar; + +/// Adds the `escape` method to `LIKE` and `NOT LIKE`. This is used to specify +/// the escape character for the pattern. +/// +/// By default, the escape character is `\` on most backends. On SQLite, +/// there is no default escape character. +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// # use diesel::insert_into; +/// # let connection = establish_connection(); +/// # insert_into(users).values(name.eq("Ha%%0r")) +/// # .execute(&connection).unwrap(); +/// let users_with_percent = users.select(name) +/// .filter(name.like("%😀%%").escape('😀')) +/// .load(&connection); +/// let users_without_percent = users.select(name) +/// .filter(name.not_like("%a%%").escape('a')) +/// .load(&connection); +/// assert_eq!(Ok(vec![String::from("Ha%%0r")]), users_with_percent); +/// assert_eq!(Ok(vec![String::from("Sean"), String::from("Tess")]), users_without_percent); +/// # } +/// ``` +pub trait EscapeExpressionMethods: Sized { + #[doc(hidden)] + type TextExpression; + + /// See the trait documentation. + fn escape(self, _character: char) -> dsl::Escape; +} + +impl EscapeExpressionMethods for Grouped> { + type TextExpression = Like; + + fn escape(self, character: char) -> dsl::Escape { + Grouped(Escape::new( + self.0, + character.to_string().into_sql::(), + )) + } +} + +impl EscapeExpressionMethods for Grouped> { + type TextExpression = NotLike; + + fn escape(self, character: char) -> dsl::Escape { + Grouped(Escape::new( + self.0, + character.to_string().into_sql::(), + )) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/expression_methods/global_expression_methods.rs b/collector/benchmarks/diesel/diesel/src/expression_methods/global_expression_methods.rs new file mode 100644 index 000000000..5ac135d11 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression_methods/global_expression_methods.rs @@ -0,0 +1,479 @@ +use crate::dsl; +use crate::expression::array_comparison::{AsInExpression, In, NotIn}; +use crate::expression::grouped::Grouped; +use crate::expression::operators::*; +use crate::expression::{nullable, AsExpression, Expression}; +use crate::sql_types::{SingleValue, SqlType}; + +/// Methods present on all expressions, except tuples +pub trait ExpressionMethods: Expression + Sized { + /// Creates a SQL `=` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let data = users.select(id).filter(name.eq("Sean")); + /// assert_eq!(Ok(1), data.first(&connection)); + /// # } + /// ``` + fn eq(self, other: T) -> dsl::Eq + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(Eq::new(self, other.as_expression())) + } + + /// Creates a SQL `!=` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let data = users.select(id).filter(name.ne("Sean")); + /// assert_eq!(Ok(2), data.first(&connection)); + /// # } + /// ``` + fn ne(self, other: T) -> dsl::NotEq + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(NotEq::new(self, other.as_expression())) + } + + /// Creates a SQL `IN` statement. + /// + /// Queries using this method will not typically be + /// placed in the prepared statement cache. However, + /// in cases when a subquery is passed to the method, that + /// query will use the cache (assuming the subquery + /// itself is safe to cache). + /// On PostgreSQL, you should use + /// `eq(any())` instead. This method may change in the future to + /// automatically perform `= ANY` on PostgreSQL. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users; + /// # use schema::posts; + /// # let connection = establish_connection(); + /// # connection.execute("INSERT INTO users (name) VALUES + /// # ('Jim')").unwrap(); + /// let data = users::table.select(users::id).filter(users::name.eq_any(vec!["Sean", "Jim"])); + /// assert_eq!(Ok(vec![1, 3]), data.load(&connection)); + /// + /// // Calling `eq_any` with an empty array is the same as doing `WHERE 1=0` + /// let data = users::table.select(users::id).filter(users::name.eq_any(Vec::::new())); + /// assert_eq!(Ok(vec![]), data.load::(&connection)); + /// + /// // Calling `eq_any` with a subquery is the same as using + /// // `WHERE {column} IN {subquery}`. + /// + /// let subquery = users::table.filter(users::name.eq("Sean")).select(users::id).into_boxed(); + /// let data = posts::table.select(posts::id).filter(posts::user_id.eq_any(subquery)); + /// assert_eq!(Ok(vec![1, 2]), data.load::(&connection)); + /// + /// # } + /// ``` + fn eq_any(self, values: T) -> dsl::EqAny + where + Self::SqlType: SqlType, + T: AsInExpression, + { + Grouped(In::new(self, values.as_in_expression())) + } + + /// Creates a SQL `NOT IN` statement. + /// + /// Queries using this method will not be + /// placed in the prepared statement cache. On PostgreSQL, you should use + /// `ne(all())` instead. This method may change in the future to + /// automatically perform `!= ALL` on PostgreSQL. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// # connection.execute("INSERT INTO users (name) VALUES + /// # ('Jim')").unwrap(); + /// let data = users.select(id).filter(name.ne_all(vec!["Sean", "Jim"])); + /// assert_eq!(Ok(vec![2]), data.load(&connection)); + /// + /// let data = users.select(id).filter(name.ne_all(vec!["Tess"])); + /// assert_eq!(Ok(vec![1, 3]), data.load(&connection)); + /// + /// // Calling `ne_any` with an empty array is the same as doing `WHERE 1=1` + /// let data = users.select(id).filter(name.ne_all(Vec::::new())); + /// assert_eq!(Ok(vec![1, 2, 3]), data.load(&connection)); + /// # } + /// ``` + fn ne_all(self, values: T) -> dsl::NeAny + where + Self::SqlType: SqlType, + T: AsInExpression, + { + Grouped(NotIn::new(self, values.as_in_expression())) + } + + /// Creates a SQL `IS NULL` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// # + /// let data = animals + /// .select(species) + /// .filter(name.is_null()) + /// .first::(&connection)?; + /// assert_eq!("spider", data); + /// # Ok(()) + /// # } + fn is_null(self) -> dsl::IsNull { + Grouped(IsNull::new(self)) + } + + /// Creates a SQL `IS NOT NULL` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// # + /// let data = animals + /// .select(species) + /// .filter(name.is_not_null()) + /// .first::(&connection)?; + /// assert_eq!("dog", data); + /// # Ok(()) + /// # } + fn is_not_null(self) -> dsl::IsNotNull { + Grouped(IsNotNull::new(self)) + } + + /// Creates a SQL `>` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let data = users + /// .select(name) + /// .filter(id.gt(1)) + /// .first::(&connection)?; + /// assert_eq!("Tess", data); + /// # Ok(()) + /// # } + /// ``` + fn gt(self, other: T) -> dsl::Gt + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(Gt::new(self, other.as_expression())) + } + + /// Creates a SQL `>=` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let data = users + /// .select(name) + /// .filter(id.ge(2)) + /// .first::(&connection)?; + /// assert_eq!("Tess", data); + /// # Ok(()) + /// # } + /// ``` + fn ge(self, other: T) -> dsl::GtEq + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(GtEq::new(self, other.as_expression())) + } + + /// Creates a SQL `<` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let data = users + /// .select(name) + /// .filter(id.lt(2)) + /// .first::(&connection)?; + /// assert_eq!("Sean", data); + /// # Ok(()) + /// # } + /// ``` + fn lt(self, other: T) -> dsl::Lt + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(Lt::new(self, other.as_expression())) + } + + /// Creates a SQL `<=` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let data = users + /// .select(name) + /// .filter(id.le(2)) + /// .first::(&connection)?; + /// assert_eq!("Sean", data); + /// # Ok(()) + /// # } + fn le(self, other: T) -> dsl::LtEq + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(LtEq::new(self, other.as_expression())) + } + + /// Creates a SQL `BETWEEN` expression using the given lower and upper + /// bounds. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// # + /// let data = animals + /// .select(species) + /// .filter(legs.between(2, 6)) + /// .first(&connection); + /// # + /// assert_eq!(Ok("dog".to_string()), data); + /// # } + /// ``` + fn between(self, lower: T, upper: U) -> dsl::Between + where + Self::SqlType: SqlType, + T: AsExpression, + U: AsExpression, + { + Grouped(Between::new( + self, + And::new(lower.as_expression(), upper.as_expression()), + )) + } + + /// Creates a SQL `NOT BETWEEN` expression using the given lower and upper + /// bounds. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// # + /// let data = animals + /// .select(species) + /// .filter(legs.not_between(2, 6)) + /// .first::(&connection)?; + /// assert_eq!("spider", data); + /// # Ok(()) + /// # } + fn not_between(self, lower: T, upper: U) -> dsl::NotBetween + where + Self::SqlType: SqlType, + T: AsExpression, + U: AsExpression, + { + Grouped(NotBetween::new( + self, + And::new(lower.as_expression(), upper.as_expression()), + )) + } + + /// Creates a SQL `DESC` expression, representing this expression in + /// descending order. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// # + /// let names = users + /// .select(name) + /// .order(name.desc()) + /// .load::(&connection)?; + /// assert_eq!(vec!["Tess", "Sean"], names); + /// # Ok(()) + /// # } + /// ``` + fn desc(self) -> dsl::Desc { + Desc::new(self) + } + + /// Creates a SQL `ASC` expression, representing this expression in + /// ascending order. + /// + /// This is the same as leaving the direction unspecified. It is useful if + /// you need to provide an unknown ordering, and need to box the return + /// value of a function. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use diesel::expression::expression_types::NotSelectable; + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let order = "name"; + /// let ordering: Box> = + /// if order == "name" { + /// Box::new(name.desc()) + /// } else { + /// Box::new(id.asc()) + /// }; + /// # } + /// ``` + fn asc(self) -> dsl::Asc { + Asc::new(self) + } +} + +impl ExpressionMethods for T +where + T: Expression, + T::SqlType: SingleValue, +{ +} + +/// Methods present on all expressions +pub trait NullableExpressionMethods: Expression + Sized { + /// Converts this potentially non-null expression into one which is treated + /// as nullable. This method has no impact on the generated SQL, and is only + /// used to allow certain comparisons that would otherwise fail to compile. + /// + /// # Example + /// ```no_run + /// # #![allow(dead_code)] + /// # include!("../doctest_setup.rs"); + /// # use diesel::sql_types::*; + /// # use schema::users; + /// # + /// table! { + /// posts { + /// id -> Integer, + /// user_id -> Integer, + /// author_name -> Nullable, + /// } + /// } + /// # + /// # joinable!(posts -> users (user_id)); + /// # allow_tables_to_appear_in_same_query!(posts, users); + /// + /// fn main() { + /// use self::users::dsl::*; + /// use self::posts::dsl::{posts, author_name}; + /// let connection = establish_connection(); + /// + /// let data = users.inner_join(posts) + /// .filter(name.nullable().eq(author_name)) + /// .select(name) + /// .load::(&connection); + /// println!("{:?}", data); + /// } + /// ``` + fn nullable(self) -> dsl::Nullable { + nullable::Nullable::new(self) + } +} + +impl NullableExpressionMethods for T {} diff --git a/collector/benchmarks/diesel/diesel/src/expression_methods/mod.rs b/collector/benchmarks/diesel/diesel/src/expression_methods/mod.rs new file mode 100644 index 000000000..f91e5d0b8 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression_methods/mod.rs @@ -0,0 +1,26 @@ +//! Adds various methods to construct new expressions. These traits are exported +//! by default, and implemented automatically. +//! +//! You can rely on the methods provided by this trait existing on any +//! `Expression` of the appropriate type. You should not rely on the specific +//! traits existing, their names, or their organization. +mod bool_expression_methods; +mod eq_all; +mod escape_expression_methods; +mod global_expression_methods; +mod text_expression_methods; + +#[doc(inline)] +pub use self::bool_expression_methods::BoolExpressionMethods; +#[doc(hidden)] +pub use self::eq_all::EqAll; +#[doc(inline)] +pub use self::escape_expression_methods::EscapeExpressionMethods; +#[doc(inline)] +pub use self::global_expression_methods::{ExpressionMethods, NullableExpressionMethods}; +#[doc(inline)] +pub use self::text_expression_methods::TextExpressionMethods; + +#[cfg(feature = "postgres")] +#[doc(inline)] +pub use crate::pg::expression::expression_methods::*; diff --git a/collector/benchmarks/diesel/diesel/src/expression_methods/text_expression_methods.rs b/collector/benchmarks/diesel/diesel/src/expression_methods/text_expression_methods.rs new file mode 100644 index 000000000..0e8170605 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression_methods/text_expression_methods.rs @@ -0,0 +1,154 @@ +use crate::dsl; +use crate::expression::grouped::Grouped; +use crate::expression::operators::{Concat, Like, NotLike}; +use crate::expression::{AsExpression, Expression}; +use crate::sql_types::{Nullable, SqlType, Text}; + +/// Methods present on text expressions +pub trait TextExpressionMethods: Expression + Sized { + /// Concatenates two strings using the `||` operator. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # hair_color -> Nullable, + /// # } + /// # } + /// # + /// # fn main() { + /// # use self::users::dsl::*; + /// # use diesel::insert_into; + /// # + /// # let connection = connection_no_data(); + /// # connection.execute("CREATE TABLE users ( + /// # id INTEGER PRIMARY KEY, + /// # name VARCHAR(255) NOT NULL, + /// # hair_color VARCHAR(255) + /// # )").unwrap(); + /// # + /// # insert_into(users) + /// # .values(&vec![ + /// # (id.eq(1), name.eq("Sean"), hair_color.eq(Some("Green"))), + /// # (id.eq(2), name.eq("Tess"), hair_color.eq(None)), + /// # ]) + /// # .execute(&connection) + /// # .unwrap(); + /// # + /// let names = users.select(name.concat(" the Greatest")).load(&connection); + /// let expected_names = vec![ + /// "Sean the Greatest".to_string(), + /// "Tess the Greatest".to_string(), + /// ]; + /// assert_eq!(Ok(expected_names), names); + /// + /// // If the value is nullable, the output will be nullable + /// let names = users.select(hair_color.concat("ish")).load(&connection); + /// let expected_names = vec![ + /// Some("Greenish".to_string()), + /// None, + /// ]; + /// assert_eq!(Ok(expected_names), names); + /// # } + /// ``` + fn concat(self, other: T) -> dsl::Concat + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(Concat::new(self, other.as_expression())) + } + + /// Returns a SQL `LIKE` expression + /// + /// This method is case insensitive for SQLite and MySQL. + /// On PostgreSQL, `LIKE` is case sensitive. You may use + /// [`ilike()`](../expression_methods/trait.PgTextExpressionMethods.html#method.ilike) + /// for case insensitive comparison on PostgreSQL. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// # + /// let starts_with_s = users + /// .select(name) + /// .filter(name.like("S%")) + /// .load::(&connection)?; + /// assert_eq!(vec!["Sean"], starts_with_s); + /// # Ok(()) + /// # } + /// ``` + fn like(self, other: T) -> dsl::Like + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(Like::new(self, other.as_expression())) + } + + /// Returns a SQL `NOT LIKE` expression + /// + /// This method is case insensitive for SQLite and MySQL. + /// On PostgreSQL `NOT LIKE` is case sensitive. You may use + /// [`not_ilike()`](../expression_methods/trait.PgTextExpressionMethods.html#method.not_ilike) + /// for case insensitive comparison on PostgreSQL. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// # + /// let doesnt_start_with_s = users + /// .select(name) + /// .filter(name.not_like("S%")) + /// .load::(&connection)?; + /// assert_eq!(vec!["Tess"], doesnt_start_with_s); + /// # Ok(()) + /// # } + /// ``` + fn not_like(self, other: T) -> dsl::NotLike + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(NotLike::new(self, other.as_expression())) + } +} + +#[doc(hidden)] +/// Marker trait used to implement `TextExpressionMethods` on the appropriate +/// types. Once coherence takes associated types into account, we can remove +/// this trait. +pub trait TextOrNullableText {} + +impl TextOrNullableText for Text {} +impl TextOrNullableText for Nullable {} + +impl TextExpressionMethods for T +where + T: Expression, + T::SqlType: TextOrNullableText, +{ +} diff --git a/collector/benchmarks/diesel/diesel/src/insertable.rs b/collector/benchmarks/diesel/diesel/src/insertable.rs new file mode 100644 index 000000000..f7fa3fa8f --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/insertable.rs @@ -0,0 +1,334 @@ +use std::marker::PhantomData; + +use crate::backend::{Backend, SupportsDefaultKeyword}; +use crate::expression::grouped::Grouped; +use crate::expression::{AppearsOnTable, Expression}; +use crate::query_builder::{ + AstPass, InsertStatement, QueryFragment, UndecoratedInsertRecord, ValuesClause, +}; +use crate::query_source::{Column, Table}; +use crate::result::QueryResult; +#[cfg(feature = "sqlite")] +use crate::sqlite::Sqlite; + +/// Represents that a structure can be used to insert a new row into the +/// database. This is automatically implemented for `&[T]` and `&Vec` for +/// inserting more than one record. +/// +/// This trait can be [derived](derive.Insertable.html) +pub trait Insertable { + /// The `VALUES` clause to insert these records + /// + /// The types used here are generally internal to Diesel. + /// Implementations of this trait should use the `Values` + /// type of other `Insertable` types. + /// For example ` as Insertable>::Values`. + type Values; + + /// Construct `Self::Values` + /// + /// Implementations of this trait typically call `.values` + /// on other `Insertable` types. + fn values(self) -> Self::Values; + + /// Insert `self` into a given table. + /// + /// `foo.insert_into(table)` is identical to `insert_into(table).values(foo)`. + /// However, when inserting from a select statement, + /// this form is generally preferred. + /// + /// # Example + /// + /// ```rust + /// # include!("doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::{posts, users}; + /// # let conn = establish_connection(); + /// # diesel::delete(posts::table).execute(&conn)?; + /// users::table + /// .select(( + /// users::name.concat("'s First Post"), + /// users::id, + /// )) + /// .insert_into(posts::table) + /// .into_columns((posts::title, posts::user_id)) + /// .execute(&conn)?; + /// + /// let inserted_posts = posts::table + /// .select(posts::title) + /// .load::(&conn)?; + /// let expected = vec!["Sean's First Post", "Tess's First Post"]; + /// assert_eq!(expected, inserted_posts); + /// # Ok(()) + /// # } + /// ``` + fn insert_into(self, table: T) -> InsertStatement + where + Self: Sized, + { + crate::insert_into(table).values(self) + } +} + +#[doc(inline)] +pub use diesel_derives::Insertable; + +pub trait CanInsertInSingleQuery { + /// How many rows will this query insert? + /// + /// This function should only return `None` when the query is valid on all + /// backends, regardless of how many rows get inserted. + fn rows_to_insert(&self) -> Option; +} + +impl<'a, T, DB> CanInsertInSingleQuery for &'a T +where + T: ?Sized + CanInsertInSingleQuery, + DB: Backend, +{ + fn rows_to_insert(&self) -> Option { + (*self).rows_to_insert() + } +} + +impl<'a, T, Tab, DB> CanInsertInSingleQuery for BatchInsert<'a, T, Tab> +where + DB: Backend + SupportsDefaultKeyword, +{ + fn rows_to_insert(&self) -> Option { + Some(self.records.len()) + } +} + +impl CanInsertInSingleQuery for OwnedBatchInsert +where + DB: Backend + SupportsDefaultKeyword, +{ + fn rows_to_insert(&self) -> Option { + Some(self.values.len()) + } +} + +impl CanInsertInSingleQuery for ColumnInsertValue +where + DB: Backend, +{ + fn rows_to_insert(&self) -> Option { + Some(1) + } +} + +pub trait InsertValues: QueryFragment { + fn column_names(&self, out: AstPass) -> QueryResult<()>; +} + +#[derive(Debug, Copy, Clone)] +#[doc(hidden)] +pub enum ColumnInsertValue { + Expression(Col, Expr), + Default, +} + +impl Default for ColumnInsertValue { + fn default() -> Self { + ColumnInsertValue::Default + } +} + +impl InsertValues for ColumnInsertValue +where + DB: Backend + SupportsDefaultKeyword, + Col: Column, + Expr: Expression + AppearsOnTable<()>, + Self: QueryFragment, +{ + fn column_names(&self, mut out: AstPass) -> QueryResult<()> { + out.push_identifier(Col::NAME)?; + Ok(()) + } +} + +impl QueryFragment for ColumnInsertValue +where + DB: Backend + SupportsDefaultKeyword, + Expr: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + if let ColumnInsertValue::Expression(_, ref value) = *self { + value.walk_ast(out.reborrow())?; + } else { + out.push_sql("DEFAULT"); + } + Ok(()) + } +} + +#[cfg(feature = "sqlite")] +impl InsertValues for ColumnInsertValue +where + Col: Column, + Expr: Expression + AppearsOnTable<()>, + Self: QueryFragment, +{ + fn column_names(&self, mut out: AstPass) -> QueryResult<()> { + if let ColumnInsertValue::Expression(..) = *self { + out.push_identifier(Col::NAME)?; + } + Ok(()) + } +} + +#[cfg(feature = "sqlite")] +impl QueryFragment for ColumnInsertValue +where + Expr: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + if let ColumnInsertValue::Expression(_, ref value) = *self { + value.walk_ast(out.reborrow())?; + } + Ok(()) + } +} + +impl<'a, T, Tab> Insertable for &'a [T] +where + &'a T: UndecoratedInsertRecord, +{ + type Values = BatchInsert<'a, T, Tab>; + + fn values(self) -> Self::Values { + BatchInsert { + records: self, + _marker: PhantomData, + } + } +} + +impl<'a, T, Tab> Insertable for &'a Vec +where + &'a [T]: Insertable, +{ + type Values = <&'a [T] as Insertable>::Values; + + fn values(self) -> Self::Values { + (&**self).values() + } +} + +impl Insertable for Vec +where + T: Insertable + UndecoratedInsertRecord, +{ + type Values = OwnedBatchInsert; + + fn values(self) -> Self::Values { + OwnedBatchInsert { + values: self.into_iter().map(Insertable::values).collect(), + _marker: PhantomData, + } + } +} + +impl Insertable for Option +where + T: Insertable, + T::Values: Default, +{ + type Values = T::Values; + + fn values(self) -> Self::Values { + self.map(Insertable::values).unwrap_or_default() + } +} + +impl<'a, T, Tab> Insertable for &'a Option +where + Option<&'a T>: Insertable, +{ + type Values = as Insertable>::Values; + + fn values(self) -> Self::Values { + self.as_ref().values() + } +} + +impl Insertable for Grouped> +where + crate::expression::operators::Eq: Insertable, +{ + type Values = as Insertable>::Values; + + fn values(self) -> Self::Values { + self.0.values() + } +} + +impl<'a, L, R, Tab> Insertable for &'a Grouped> +where + &'a crate::expression::operators::Eq: Insertable, +{ + type Values = <&'a crate::expression::operators::Eq as Insertable>::Values; + + fn values(self) -> Self::Values { + self.0.values() + } +} + +#[derive(Debug, Clone, Copy)] +pub struct BatchInsert<'a, T: 'a, Tab> { + pub records: &'a [T], + _marker: PhantomData, +} + +impl<'a, T, Tab, Inner, DB> QueryFragment for BatchInsert<'a, T, Tab> +where + DB: Backend + SupportsDefaultKeyword, + &'a T: Insertable>, + ValuesClause: QueryFragment, + Inner: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + let mut records = self.records.iter().map(Insertable::values); + if let Some(record) = records.next() { + record.walk_ast(out.reborrow())?; + } + for record in records { + out.push_sql(", ("); + record.values.walk_ast(out.reborrow())?; + out.push_sql(")"); + } + Ok(()) + } +} + +#[derive(Debug)] +pub struct OwnedBatchInsert { + pub values: Vec, + _marker: PhantomData, +} + +impl QueryFragment for OwnedBatchInsert, Tab> +where + DB: Backend + SupportsDefaultKeyword, + ValuesClause: QueryFragment, + Inner: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + let mut values = self.values.iter(); + if let Some(value) = values.next() { + value.walk_ast(out.reborrow())?; + } + for value in values { + out.push_sql(", ("); + value.values.walk_ast(out.reborrow())?; + out.push_sql(")"); + } + Ok(()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/lib.rs b/collector/benchmarks/diesel/diesel/src/lib.rs new file mode 100644 index 000000000..3e924fb11 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/lib.rs @@ -0,0 +1,368 @@ +//! # Diesel +//! +//! Diesel is an ORM and query builder designed to reduce the boilerplate for database interactions. +//! If this is your first time reading this documentation, +//! we recommend you start with the [getting started guide]. +//! We also have [many other long form guides]. +//! +//! [getting started guide]: https://diesel.rs/guides/getting-started/ +//! [many other long form guides]: https://diesel.rs/guides +//! +//! # Where to find things +//! +//! ## Declaring your schema +//! +//! For Diesel to validate your queries at compile time +//! it requires you to specify your schema in your code, +//! which you can do with [the `table!` macro][`table!`]. +//! `diesel print-schema` can be used +//! to automatically generate these macro calls +//! (by connecting to your database and querying its schema). +//! +//! [`table!`]: macro.table.html +//! +//! ## Getting started +//! +//! Queries usually start from either a table, or a function like [`update`]. +//! Those functions can be found [here](#functions). +//! +//! Diesel provides a [`prelude` module](prelude), +//! which exports most of the typically used traits and types. +//! We are conservative about what goes in this module, +//! and avoid anything which has a generic name. +//! Files which use Diesel are expected to have `use diesel::prelude::*;`. +//! +//! [`update`]: fn.update.html +//! +//! ## Constructing a query +//! +//! The tools the query builder gives you can be put into these three categories: +//! +//! - "Query builder methods" are things that map to portions of a whole query +//! (such as `ORDER` and `WHERE`). These methods usually have the same name +//! as the SQL they map to, except for `WHERE` which is called `filter` in Diesel +//! (To not conflict with the Rust keyword). +//! These methods live in [the `query_dsl` module](query_dsl). +//! - "Expression methods" are things you would call on columns +//! or other individual values. +//! These methods live in [the `expression_methods` module](expression_methods) +//! You can often find these by thinking "what would this be called" +//! if it were a method +//! and typing that into the search bar +//! (e.g. `LIKE` is called `like` in Diesel). +//! Most operators are named based on the Rust function which maps to that +//! operator in [`std::ops`][] +//! (For example `==` is called `.eq`, and `!=` is called `.ne`). +//! - "Bare functions" are normal SQL functions +//! such as `sum`. +//! They live in [the `dsl` module](dsl). +//! Diesel only supports a very small number of these functions. +//! You can declare additional functions you want to use +//! with [the `sql_function!` macro][`sql_function!`]. +//! +//! [`std::ops`]: //doc.rust-lang.org/stable/std/ops/index.html +//! [`sql_function!`]: macro.sql_function.html +//! +//! ## Serializing and Deserializing +//! +//! Types which represent the result of a SQL query implement +//! a trait called [`Queryable`]. +//! +//! Diesel maps "Rust types" (e.g. `i32`) to and from "SQL types" +//! (e.g. [`diesel::sql_types::Integer`]). +//! You can find all the types supported by Diesel in [the `sql_types` module](sql_types). +//! These types are only used to represent a SQL type. +//! You should never put them on your `Queryable` structs. +//! +//! To find all the Rust types which can be used with a given SQL type, +//! see the documentation for that SQL type. +//! +//! To find all the SQL types which can be used with a Rust type, +//! go to the docs for either [`ToSql`] or [`FromSql`], +//! go to the "Implementors" section, +//! and find the Rust type you want to use. +//! +//! [`Queryable`]: deserialize/trait.Queryable.html +//! [`diesel::sql_types::Integer`]: sql_types/struct.Integer.html +//! [`ToSql`]: serialize/trait.ToSql.html +//! [`FromSql`]: deserialize/trait.FromSql.html +//! +//! ## Getting help +//! +//! If you run into problems, Diesel has a very active Gitter room. +//! You can come ask for help at +//! [gitter.im/diesel-rs/diesel](https://gitter.im/diesel-rs/diesel) + +#![recursion_limit="1024"] +#![cfg_attr(feature = "unstable", feature(specialization, trait_alias))] +// For the `specialization` feature. +#![cfg_attr(feature = "unstable", allow(incomplete_features))] +// Built-in Lints +#![deny(warnings)] +#![warn( + missing_debug_implementations, + missing_copy_implementations, + missing_docs +)] +// Clippy lints +#![allow( + clippy::match_same_arms, + clippy::needless_doctest_main, + clippy::option_map_unwrap_or_else, + clippy::option_map_unwrap_or, + clippy::redundant_field_names, + clippy::type_complexity +)] +#![cfg_attr(test, allow(clippy::option_map_unwrap_or, clippy::result_unwrap_used))] +#![warn( + clippy::option_unwrap_used, + clippy::result_unwrap_used, + clippy::print_stdout, + clippy::wrong_pub_self_convention, + clippy::mut_mut, + clippy::non_ascii_literal, + clippy::similar_names, + clippy::unicode_not_nfc, + clippy::enum_glob_use, + clippy::if_not_else, + clippy::items_after_statements, + clippy::used_underscore_binding +)] + +#[cfg(feature = "postgres")] +#[macro_use] +extern crate bitflags; +extern crate byteorder; +extern crate diesel_derives; + +#[macro_use] +#[doc(hidden)] +pub mod macros; + +#[cfg(test)] +#[macro_use] +extern crate cfg_if; + +#[cfg(test)] +pub mod test_helpers; + +pub mod associations; +pub mod backend; +pub mod connection; +pub mod data_types; +pub mod deserialize; +#[macro_use] +pub mod expression; +pub mod expression_methods; +#[doc(hidden)] +pub mod insertable; +pub mod query_builder; +pub mod query_dsl; +pub mod query_source; +#[cfg(feature = "r2d2")] +pub mod r2d2; +pub mod result; +pub mod serialize; +pub mod upsert; +#[macro_use] +pub mod sql_types; +pub mod migration; +pub mod row; + +#[cfg(feature = "mysql")] +pub mod mysql; +#[cfg(feature = "postgres")] +pub mod pg; +#[cfg(feature = "sqlite")] +pub mod sqlite; + +mod type_impls; +mod util; + +#[doc(hidden)] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +#[deprecated(since = "2.0.0", note = "Use explicit macro imports instead")] +pub use diesel_derives::*; + +pub mod dsl { + //! Includes various helper types and bare functions which are named too + //! generically to be included in prelude, but are often used when using Diesel. + + #[doc(inline)] + pub use crate::helper_types::*; + + #[doc(inline)] + pub use crate::expression::dsl::*; + + #[doc(inline)] + pub use crate::query_builder::functions::{ + delete, insert_into, insert_or_ignore_into, replace_into, select, sql_query, update, + }; +} + +pub mod helper_types { + //! Provide helper types for concisely writing the return type of functions. + //! As with iterators, it is unfortunately difficult to return a partially + //! constructed query without exposing the exact implementation of the + //! function. Without higher kinded types, these various DSLs can't be + //! combined into a single trait for boxing purposes. + //! + //! All types here are in the form `>::Output`. So the return type of + //! `users.filter(first_name.eq("John")).order(last_name.asc()).limit(10)` would + //! be `Limit, Asc>>` + use super::query_builder::locking_clause as lock; + use super::query_dsl::methods::*; + use super::query_dsl::*; + use super::query_source::joins; + + #[doc(inline)] + pub use crate::expression::helper_types::*; + + /// Represents the return type of `.select(selection)` + pub type Select = >::Output; + + /// Represents the return type of `.filter(predicate)` + pub type Filter = >::Output; + + /// Represents the return type of `.filter(lhs.eq(rhs))` + pub type FindBy = Filter>; + + /// Represents the return type of `.for_update()` + pub type ForUpdate = >::Output; + + /// Represents the return type of `.for_no_key_update()` + pub type ForNoKeyUpdate = >::Output; + + /// Represents the return type of `.for_share()` + pub type ForShare = >::Output; + + /// Represents the return type of `.for_key_share()` + pub type ForKeyShare = >::Output; + + /// Represents the return type of `.skip_locked()` + pub type SkipLocked = >::Output; + + /// Represents the return type of `.no_wait()` + pub type NoWait = >::Output; + + /// Represents the return type of `.find(pk)` + pub type Find = >::Output; + + /// Represents the return type of `.or_filter(predicate)` + pub type OrFilter = >::Output; + + /// Represents the return type of `.order(ordering)` + pub type Order = >::Output; + + /// Represents the return type of `.then_order_by(ordering)` + pub type ThenOrderBy = >::Output; + + /// Represents the return type of `.limit()` + pub type Limit = ::Output; + + /// Represents the return type of `.offset()` + pub type Offset = ::Output; + + /// Represents the return type of `.inner_join(rhs)` + pub type InnerJoin = + >::Output; + + /// Represents the return type of `.inner_join(rhs.on(on))` + pub type InnerJoinOn = + >::Output; + + /// Represents the return type of `.left_join(rhs)` + pub type LeftJoin = + >::Output; + + /// Represents the return type of `.left_join(rhs.on(on))` + pub type LeftJoinOn = + >::Output; + + use super::associations::HasTable; + use super::query_builder::{AsChangeset, IntoUpdateTarget, UpdateStatement}; + /// Represents the return type of `update(lhs).set(rhs)` + pub type Update = UpdateStatement< + ::Table, + ::WhereClause, + ::Changeset, + >; + + /// Represents the return type of `.into_boxed::<'a, DB>()` + pub type IntoBoxed<'a, Source, DB> = >::Output; + + /// Represents the return type of `.distinct()` + pub type Distinct = ::Output; + + /// Represents the return type of `.distinct_on(expr)` + #[cfg(feature = "postgres")] + pub type DistinctOn = >::Output; + + /// Represents the return type of `.single_value()` + pub type SingleValue = ::Output; + + /// Represents the return type of `.nullable()` + pub type NullableSelect = ::Output; + + /// Represents the return type of `.group_by(expr)` + pub type GroupBy = >::Output; +} + +pub mod prelude { + //! Re-exports important traits and types. Meant to be glob imported when using Diesel. + + #[doc(inline)] + pub use crate::associations::{Associations, GroupedBy, Identifiable}; + #[doc(inline)] + pub use crate::connection::Connection; + #[doc(inline)] + pub use crate::deserialize::{Queryable, QueryableByName}; + #[doc(inline)] + pub use crate::expression::{ + AppearsOnTable, BoxableExpression, Expression, IntoSql, SelectableExpression, + }; + + #[doc(inline)] + pub use crate::expression::functions::sql_function; + + #[doc(inline)] + pub use crate::expression_methods::*; + #[doc(inline)] + pub use crate::insertable::Insertable; + #[doc(inline)] + pub use crate::macros::prelude::*; + #[doc(inline)] + pub use crate::query_builder::AsChangeset; + #[doc(inline)] + pub use crate::query_builder::DecoratableTarget; + #[doc(inline)] + pub use crate::query_dsl::{BelongingToDsl, JoinOnDsl, QueryDsl, RunQueryDsl, SaveChangesDsl}; + #[doc(inline)] + pub use crate::query_source::{Column, JoinTo, QuerySource, Table}; + #[doc(inline)] + pub use crate::result::{ConnectionError, ConnectionResult, OptionalExtension, QueryResult}; + + #[cfg(feature = "mysql")] + #[doc(inline)] + pub use crate::mysql::MysqlConnection; + #[cfg(feature = "postgres")] + #[doc(inline)] + pub use crate::pg::PgConnection; + #[cfg(feature = "sqlite")] + #[doc(inline)] + pub use crate::sqlite::SqliteConnection; +} + +pub use crate::prelude::*; +#[doc(inline)] +pub use crate::query_builder::debug_query; +#[doc(inline)] +pub use crate::query_builder::functions::{ + delete, insert_into, insert_or_ignore_into, replace_into, select, sql_query, update, +}; +pub use crate::result::Error::NotFound; + +pub(crate) mod diesel { + pub use super::*; +} diff --git a/collector/benchmarks/diesel/diesel/src/macros/internal.rs b/collector/benchmarks/diesel/diesel/src/macros/internal.rs new file mode 100644 index 000000000..71892ffc6 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/macros/internal.rs @@ -0,0 +1,36 @@ +/// This will implement `SelectableExpression` and `AppearsOnTable` for "simple" +/// composite nodes where the where clause is roughly `AllTyParams: +/// SelectableExpression, Self: Expression`. +/// +/// This macro is exported because we want to be able to call it from other +/// macros that are exported, but it is not part of our public API. +#[macro_export] +#[doc(hidden)] +macro_rules! impl_selectable_expression { + ($struct_name:ident) => { + $crate::impl_selectable_expression!(ty_params = (), struct_ty = $struct_name,); + }; + + ($struct_name:ident<$($ty_params:ident),+>) => { + $crate::impl_selectable_expression!( + ty_params = ($($ty_params),+), + struct_ty = $struct_name<$($ty_params),+>, + ); + }; + + (ty_params = ($($ty_params:ident),*), struct_ty = $struct_ty:ty,) => { + impl<$($ty_params,)* QS> $crate::expression::SelectableExpression + for $struct_ty where + $struct_ty: $crate::expression::AppearsOnTable, + $($ty_params: $crate::expression::SelectableExpression,)* + { + } + + impl<$($ty_params,)* QS> $crate::expression::AppearsOnTable + for $struct_ty where + $struct_ty: $crate::expression::Expression, + $($ty_params: $crate::expression::AppearsOnTable,)* + { + } + }; +} diff --git a/collector/benchmarks/diesel/diesel/src/macros/mod.rs b/collector/benchmarks/diesel/diesel/src/macros/mod.rs new file mode 100644 index 000000000..90bdaf33b --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/macros/mod.rs @@ -0,0 +1,1509 @@ +#![allow(unused_parens)] // FIXME: Remove this attribute once false positive is resolved. +#![cfg_attr(rustfmt, rustfmt_skip)] // https://github.com/rust-lang-nursery/rustfmt/issues/2755 + +pub(crate) mod prelude { + #[doc(inline)] + pub use crate::{ + allow_columns_to_appear_in_same_group_by_clause, + allow_tables_to_appear_in_same_query, + joinable, + table, + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_column { + ( + table = $table:ident, + name = $column_name:ident, + sql_name = $sql_name:expr, + ty = ($($Type:tt)*), + meta = [$($meta:tt)*], + ) => { + $($meta)* + #[allow(non_camel_case_types, dead_code)] + #[derive(Debug, Clone, Copy, $crate::query_builder::QueryId, Default)] + pub struct $column_name; + + impl $crate::expression::Expression for $column_name { + type SqlType = $($Type)*; + } + + impl $crate::query_builder::QueryFragment for $column_name where + DB: $crate::backend::Backend, + <$table as $crate::QuerySource>::FromClause: $crate::query_builder::QueryFragment, + { + #[allow(non_snake_case)] + fn walk_ast(&self, mut __out: $crate::query_builder::AstPass) -> $crate::result::QueryResult<()> { + use $crate::QuerySource; + $table.from_clause().walk_ast(__out.reborrow())?; + __out.push_sql("."); + __out.push_identifier($sql_name) + } + } + + impl $crate::SelectableExpression<$table> for $column_name { + } + + impl $crate::AppearsOnTable for $column_name where + QS: $crate::query_source::AppearsInFromClause<$table, Count=$crate::query_source::Once>, + { + } + + impl $crate::SelectableExpression< + $crate::query_source::joins::Join, + > for $column_name where + $column_name: $crate::AppearsOnTable<$crate::query_source::joins::Join>, + Self: $crate::SelectableExpression, + // If our table is on the right side of this join, only + // `Nullable` can be selected + Right: $crate::query_source::AppearsInFromClause<$table, Count=$crate::query_source::Never>, + { + } + + impl $crate::SelectableExpression< + $crate::query_source::joins::Join, + > for $column_name where + $column_name: $crate::AppearsOnTable<$crate::query_source::joins::Join>, + Left: $crate::query_source::AppearsInFromClause<$table>, + Right: $crate::query_source::AppearsInFromClause<$table>, + (Left::Count, Right::Count): $crate::query_source::Pick, + Self: $crate::SelectableExpression< + <(Left::Count, Right::Count) as $crate::query_source::Pick>::Selection, + >, + { + } + + // FIXME: Remove this when overlapping marker traits are stable + impl $crate::SelectableExpression<$crate::query_source::joins::JoinOn> for $column_name where + $column_name: $crate::SelectableExpression + $crate::AppearsOnTable<$crate::query_source::joins::JoinOn>, + { + } + + // FIXME: Remove this when overlapping marker traits are stable + impl $crate::SelectableExpression<$crate::query_builder::SelectStatement> for $column_name where + $column_name: $crate::SelectableExpression + $crate::AppearsOnTable<$crate::query_builder::SelectStatement>, + { + } + + impl<__GB> $crate::expression::ValidGrouping<__GB> for $column_name + where __GB: $crate::expression::IsContainedInGroupBy<$column_name, Output = $crate::expression::is_contained_in_group_by::Yes>, + { + type IsAggregate = $crate::expression::is_aggregate::Yes; + } + + impl $crate::expression::ValidGrouping<()> for $column_name { + type IsAggregate = $crate::expression::is_aggregate::No; + } + + impl $crate::expression::IsContainedInGroupBy<$column_name> for $column_name { + type Output = $crate::expression::is_contained_in_group_by::Yes; + } + + impl $crate::query_source::Column for $column_name { + type Table = $table; + + const NAME: &'static str = $sql_name; + } + + impl $crate::EqAll for $column_name where + T: $crate::expression::AsExpression<$($Type)*>, + $crate::dsl::Nullable<$crate::dsl::Eq<$column_name, T::Expression>>: + $crate::Expression>, + { + type Output = $crate::dsl::Nullable<$crate::dsl::Eq>; + + fn eq_all(self, rhs: T) -> Self::Output { + use $crate::expression_methods::ExpressionMethods; + use $crate::expression_methods::NullableExpressionMethods; + + self.eq(rhs).nullable() + } + } + + $crate::__diesel_generate_ops_impls_if_numeric!($column_name, $($Type)*); + $crate::__diesel_generate_ops_impls_if_date_time!($column_name, $($Type)*); + $crate::__diesel_generate_ops_impls_if_network!($column_name, $($Type)*); + } +} + +/// Specifies that a table exists, and what columns it has. This will create a +/// new public module, with the same name, as the name of the table. In this +/// module, you'll find a unit struct named `table`, and a unit struct with the +/// names of each of the columns. +/// +/// By default this allows a maximum of 32 columns per table. +/// You can increase this limit to 64 by enabling the `64-column-tables` feature. +/// You can increase it to 128 by enabling the `128-column-tables` feature. +/// You can decrease it to 16 columns, +/// which improves compilation time, +/// by disabling the default features of Diesel. +/// Note that enabling 64 column tables or larger will substantially increase +/// the compile time of Diesel. +/// +/// Example usage +/// ------------- +/// +/// ```rust +/// diesel::table! { +/// users { +/// id -> Integer, +/// name -> VarChar, +/// favorite_color -> Nullable, +/// } +/// } +/// ``` +/// +/// You may also specify a primary key if it's called something other than `id`. +/// Tables with no primary key are not supported. +/// +/// ```rust +/// diesel::table! { +/// users (non_standard_primary_key) { +/// non_standard_primary_key -> Integer, +/// name -> VarChar, +/// favorite_color -> Nullable, +/// } +/// } +/// ``` +/// +/// For tables with composite primary keys, list all of the columns in the +/// primary key. +/// +/// ```rust +/// diesel::table! { +/// followings (user_id, post_id) { +/// user_id -> Integer, +/// post_id -> Integer, +/// favorited -> Bool, +/// } +/// } +/// # fn main() { +/// # use diesel::prelude::Table; +/// # use self::followings::dsl::*; +/// # // Poor man's assert_eq! -- since this is type level this would fail +/// # // to compile if the wrong primary key were generated +/// # let (user_id {}, post_id {}) = followings.primary_key(); +/// # } +/// ``` +/// +/// If you are using types that aren't from Diesel's core types, you can specify +/// which types to import. +/// +/// ``` +/// # mod diesel_full_text_search { +/// # #[derive(diesel::sql_types::SqlType)] +/// # pub struct TsVector; +/// # } +/// +/// diesel::table! { +/// use diesel::sql_types::*; +/// # use crate::diesel_full_text_search::*; +/// # /* +/// use diesel_full_text_search::*; +/// # */ +/// +/// posts { +/// id -> Integer, +/// title -> Text, +/// keywords -> TsVector, +/// } +/// } +/// # fn main() {} +/// ``` +/// +/// If you want to add documentation to the generated code you can use the +/// following syntax: +/// +/// ``` +/// diesel::table! { +/// /// The table containing all blog posts +/// posts { +/// /// The post's unique id +/// id -> Integer, +/// /// The post's title +/// title -> Text, +/// } +/// } +/// ``` +/// +/// If you have a column with the same name as a Rust reserved keyword, you can use +/// the `sql_name` attribute like this: +/// +/// ``` +/// diesel::table! { +/// posts { +/// id -> Integer, +/// /// This column is named `mytype` but references the table `type` column. +/// #[sql_name = "type"] +/// mytype -> Text, +/// } +/// } +/// ``` +/// +/// This module will also contain several helper types: +/// +/// dsl +/// --- +/// +/// This simply re-exports the table, renamed to the same name as the module, +/// and each of the columns. This is useful to glob import when you're dealing +/// primarily with one table, to allow writing `users.filter(name.eq("Sean"))` +/// instead of `users::table.filter(users::name.eq("Sean"))`. +/// +/// `all_columns` +/// ----------- +/// +/// A constant will be assigned called `all_columns`. This is what will be +/// selected if you don't otherwise specify a select clause. It's type will be +/// `table::AllColumns`. You can also get this value from the +/// `Table::all_columns` function. +/// +/// star +/// ---- +/// +/// This will be the qualified "star" expression for this table (e.g. +/// `users.*`). Internally, we read columns by index, not by name, so this +/// column is not safe to read data out of, and it has had it's SQL type set to +/// `()` to prevent accidentally using it as such. It is sometimes useful for +/// count statements however. It can also be accessed through the `Table.star()` +/// method. +/// +/// `SqlType` +/// ------- +/// +/// A type alias called `SqlType` will be created. It will be the SQL type of +/// `all_columns`. The SQL type is needed for things like [returning boxed +/// queries][boxed_queries]. +/// +/// [boxed_queries]: query_dsl/trait.QueryDsl.html#method.into_boxed +/// +/// `BoxedQuery` +/// ---------- +/// +/// ```ignore +/// pub type BoxedQuery<'a, DB, ST = SqlType> = BoxedSelectStatement<'a, ST, table, DB>; +/// ``` +#[macro_export] +macro_rules! table { + ($($tokens:tt)*) => { + $crate::__diesel_parse_table! { + tokens = [$($tokens)*], + imports = [], + meta = [], + sql_name = unknown, + name = unknown, + schema = public, + primary_key = id, + } + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_invalid_table_syntax { + () => { + compile_error!( + "Invalid `table!` syntax. Please see the `table!` macro docs for more info." + ); + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_parse_table { + // Found an import + ( + tokens = [use $($import:tt)::+; $($rest:tt)*], + imports = [$($imports:tt)*], + $($args:tt)* + ) => { + $crate::__diesel_parse_table! { + tokens = [$($rest)*], + imports = [$($imports)* use $($import)::+;], + $($args)* + } + }; + + // Found sql_name attribute, override whatever we had before + ( + tokens = [#[sql_name = $sql_name:expr] $($rest:tt)*], + imports = $imports:tt, + meta = $meta:tt, + sql_name = $ignore:tt, + $($args:tt)* + ) => { + $crate::__diesel_parse_table! { + tokens = [$($rest)*], + imports = $imports, + meta = $meta, + sql_name = $sql_name, + $($args)* + } + }; + + // Meta item other than sql_name, attach it to the table struct + ( + tokens = [#$new_meta:tt $($rest:tt)*], + imports = $imports:tt, + meta = [$($meta:tt)*], + $($args:tt)* + ) => { + $crate::__diesel_parse_table! { + tokens = [$($rest)*], + imports = $imports, + meta = [$($meta)* #$new_meta], + $($args)* + } + }; + + // Found a schema name, override whatever we had before + ( + tokens = [$schema:ident . $($rest:tt)*], + imports = $imports:tt, + meta = $meta:tt, + sql_name = $sql_name:tt, + name = $name:tt, + schema = $ignore:tt, + $($args:tt)* + ) => { + $crate::__diesel_parse_table! { + tokens = [$($rest)*], + imports = $imports, + meta = $meta, + sql_name = $sql_name, + name = $name, + schema = $schema, + $($args)* + } + }; + + // Found a table name, override whatever we had before + ( + tokens = [$name:ident $($rest:tt)*], + imports = $imports:tt, + meta = $meta:tt, + sql_name = $sql_name:tt, + name = $ignore:tt, + $($args:tt)* + ) => { + $crate::__diesel_parse_table! { + tokens = [$($rest)*], + imports = $imports, + meta = $meta, + sql_name = $sql_name, + name = $name, + $($args)* + } + }; + + // Found a primary key, override whatever we had before + ( + tokens = [($($pk:ident),+ $(,)*) $($rest:tt)*], + imports = $imports:tt, + meta = $meta:tt, + sql_name = $sql_name:tt, + name = $name:tt, + schema = $schema:tt, + primary_key = $ignore:tt, + $($args:tt)* + ) => { + $crate::__diesel_parse_table! { + tokens = [$($rest)*], + imports = $imports, + meta = $meta, + sql_name = $sql_name, + name = $name, + schema = $schema, + primary_key = ($($pk),+), + $($args)* + } + }; + + // Reached columns with no imports, set a default + ( + tokens = [{$($columns:tt)*}], + imports = [], + $($args:tt)* + ) => { + $crate::__diesel_parse_table! { + tokens = [{$($columns)*}], + imports = [use $crate::sql_types::*;], + $($args)* + } + }; + + // Reached columns with no sql_name, set a default + ( + tokens = [{$($columns:tt)*}], + imports = $imports:tt, + meta = $meta:tt, + sql_name = unknown, + name = $name:tt, + $($args:tt)* + ) => { + $crate::__diesel_parse_table! { + tokens = [{$($columns)*}], + imports = $imports, + meta = $meta, + sql_name = stringify!($name), + name = $name, + $($args)* + } + }; + + // Parse the columns + ( + tokens = [{$($columns:tt)*}], + $($args:tt)* + ) => { + $crate::__diesel_parse_columns! { + tokens = [$($columns)*], + table = { $($args)* }, + columns = [], + } + }; + + // Invalid syntax + ($($tokens:tt)*) => { + $crate::__diesel_invalid_table_syntax!(); + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_parse_columns { + // No column being parsed, start a new one. + // Attempt to capture the type as separate tokens if at all possible. + ( + tokens = [ + $(#$meta:tt)* + $name:ident -> $($ty:tt)::* $(<$($ty_params:tt)::*>)*, + $($rest:tt)* + ], + $($args:tt)* + ) => { + $crate::__diesel_parse_columns! { + current_column = { + unchecked_meta = [$(#$meta)*], + name = $name, + sql_name = stringify!($name), + ty = ($($ty)::* $(<$($ty_params)::*>)*), + meta = [], + }, + tokens = [$($rest)*], + $($args)* + } + }; + + // No column being parsed, start a new one. Couldn't keep the `ty` separate. + ( + tokens = [ + $(#$meta:tt)* + $name:ident -> $ty:ty, + $($rest:tt)* + ], + $($args:tt)* + ) => { + $crate::__diesel_parse_columns! { + current_column = { + unchecked_meta = [$(#$meta)*], + name = $name, + sql_name = stringify!($name), + ty = ($ty), + meta = [], + }, + tokens = [$($rest)*], + $($args)* + } + }; + + + // Found #[sql_name] + ( + current_column = { + unchecked_meta = [#[sql_name = $sql_name:expr] $($meta:tt)*], + name = $name:tt, + sql_name = $ignore:expr, + $($current_column:tt)* + }, + $($args:tt)* + ) => { + $crate::__diesel_parse_columns! { + current_column = { + unchecked_meta = [$($meta)*], + name = $name, + sql_name = $sql_name, + $($current_column)* + }, + $($args)* + } + }; + + // Meta item other than #[sql_name] + ( + current_column = { + unchecked_meta = [#$new_meta:tt $($unchecked_meta:tt)*], + name = $name:tt, + sql_name = $sql_name:expr, + ty = $ty:tt, + meta = [$($meta:tt)*], + $($current_column:tt)* + }, + $($args:tt)* + ) => { + $crate::__diesel_parse_columns! { + current_column = { + unchecked_meta = [$($unchecked_meta)*], + name = $name, + sql_name = $sql_name, + ty = $ty, + meta = [$($meta)* #$new_meta], + $($current_column)* + }, + $($args)* + } + }; + + // Done parsing this column + ( + current_column = { + unchecked_meta = [], + $($current_column:tt)* + }, + tokens = $tokens:tt, + table = $table:tt, + columns = [$($columns:tt,)*], + $($args:tt)* + ) => { + $crate::__diesel_parse_columns! { + tokens = $tokens, + table = $table, + columns = [$($columns,)* { $($current_column)* },], + $($args)* + } + }; + + // Done parsing all columns + ( + tokens = [], + $($args:tt)* + ) => { + $crate::__diesel_table_impl!($($args)*); + }; + + // Invalid syntax + ($($tokens:tt)*) => { + $crate::__diesel_invalid_table_syntax!(); + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_table_impl { + ( + table = { + imports = [$($imports:tt)*], + meta = [$($meta:tt)*], + sql_name = $sql_name:expr, + name = $table_name:ident, + schema = $schema:ident, + primary_key = $primary_key:tt, + }, + columns = [$({ + name = $column_name:ident, + sql_name = $column_sql_name:expr, + ty = ($($column_ty:tt)*), + $($column:tt)* + },)+], + ) => { + $($meta)* + pub mod $table_name { + #![allow(dead_code)] + $($imports)* + pub use self::columns::*; + + /// Re-exports all of the columns of this table, as well as the + /// table struct renamed to the module name. This is meant to be + /// glob imported for functions which only deal with one table. + pub mod dsl { + $($crate::static_cond! { + if $table_name == $column_name { + compile_error!(concat!( + "Column `", + stringify!($column_name), + "` cannot be named the same as its table.\n \ + You may use `#[sql_name = \"", + stringify!($column_name), + "\"]` to reference the table's `", + stringify!($column_name), + "` column. \n \ + Docs available at: `https://docs.diesel.rs/diesel/macro.table.html`\n" + )); + } else { + pub use super::columns::{$column_name}; + } + })+ + pub use super::table as $table_name; + } + + #[allow(non_upper_case_globals, dead_code)] + /// A tuple of all of the columns on this table + pub const all_columns: ($($column_name,)+) = ($($column_name,)+); + + #[allow(non_camel_case_types)] + #[derive(Debug, Clone, Copy, $crate::query_builder::QueryId)] + /// The actual table struct + /// + /// This is the type which provides the base methods of the query + /// builder, such as `.select` and `.filter`. + pub struct table; + + impl table { + #[allow(dead_code)] + /// Represents `table_name.*`, which is sometimes necessary + /// for efficient count queries. It cannot be used in place of + /// `all_columns` + pub fn star(&self) -> star { + star + } + } + + /// The SQL type of all of the columns on this table + pub type SqlType = ($($($column_ty)*,)+); + + /// Helper type for representing a boxed query from this table + pub type BoxedQuery<'a, DB, ST = SqlType> = $crate::query_builder::BoxedSelectStatement<'a, ST, table, DB>; + + $crate::__diesel_table_query_source_impl!(table, $schema, $sql_name); + + impl $crate::query_builder::AsQuery for table { + type SqlType = SqlType; + type Query = $crate::query_builder::SelectStatement; + + fn as_query(self) -> Self::Query { + $crate::query_builder::SelectStatement::simple(self) + } + } + + impl $crate::Table for table { + type PrimaryKey = $primary_key; + type AllColumns = ($($column_name,)+); + + fn primary_key(&self) -> Self::PrimaryKey { + $primary_key + } + + fn all_columns() -> Self::AllColumns { + ($($column_name,)+) + } + } + + impl $crate::associations::HasTable for table { + type Table = Self; + + fn table() -> Self::Table { + table + } + } + + impl $crate::query_builder::IntoUpdateTarget for table { + type WhereClause = <::Query as $crate::query_builder::IntoUpdateTarget>::WhereClause; + + fn into_update_target(self) -> $crate::query_builder::UpdateTarget { + use $crate::query_builder::AsQuery; + self.as_query().into_update_target() + } + } + + impl $crate::query_source::AppearsInFromClause
for table { + type Count = $crate::query_source::Once; + } + + impl $crate::query_source::AppearsInFromClause
for () { + type Count = $crate::query_source::Never; + } + + impl $crate::JoinTo<$crate::query_source::joins::Join> for table where + $crate::query_source::joins::Join: $crate::JoinTo
, + { + type FromClause = $crate::query_source::joins::Join; + type OnClause = <$crate::query_source::joins::Join as $crate::JoinTo
>::OnClause; + + fn join_target(rhs: $crate::query_source::joins::Join) -> (Self::FromClause, Self::OnClause) { + let (_, on_clause) = $crate::query_source::joins::Join::join_target(table); + (rhs, on_clause) + } + } + + impl $crate::JoinTo<$crate::query_source::joins::JoinOn> for table where + $crate::query_source::joins::JoinOn: $crate::JoinTo
, + { + type FromClause = $crate::query_source::joins::JoinOn; + type OnClause = <$crate::query_source::joins::JoinOn as $crate::JoinTo
>::OnClause; + + fn join_target(rhs: $crate::query_source::joins::JoinOn) -> (Self::FromClause, Self::OnClause) { + let (_, on_clause) = $crate::query_source::joins::JoinOn::join_target(table); + (rhs, on_clause) + } + } + + impl $crate::JoinTo<$crate::query_builder::SelectStatement> for table where + $crate::query_builder::SelectStatement: $crate::JoinTo
, + { + type FromClause = $crate::query_builder::SelectStatement; + type OnClause = <$crate::query_builder::SelectStatement as $crate::JoinTo
>::OnClause; + + fn join_target(rhs: $crate::query_builder::SelectStatement) -> (Self::FromClause, Self::OnClause) { + let (_, on_clause) = $crate::query_builder::SelectStatement::join_target(table); + (rhs, on_clause) + } + } + + impl<'a, QS, ST, DB> $crate::JoinTo<$crate::query_builder::BoxedSelectStatement<'a, QS, ST, DB>> for table where + $crate::query_builder::BoxedSelectStatement<'a, QS, ST, DB>: $crate::JoinTo
, + { + type FromClause = $crate::query_builder::BoxedSelectStatement<'a, QS, ST, DB>; + type OnClause = <$crate::query_builder::BoxedSelectStatement<'a, QS, ST, DB> as $crate::JoinTo
>::OnClause; + fn join_target(rhs: $crate::query_builder::BoxedSelectStatement<'a, QS, ST, DB>) -> (Self::FromClause, Self::OnClause) { + let (_, on_clause) = $crate::query_builder::BoxedSelectStatement::join_target(table); + (rhs, on_clause) + } + } + + // This impl should be able to live in Diesel, + // but Rust tries to recurse for no reason + impl $crate::insertable::Insertable for table + where +
::Query: $crate::insertable::Insertable, + { + type Values = <
::Query as $crate::insertable::Insertable>::Values; + + fn values(self) -> Self::Values { + use $crate::query_builder::AsQuery; + self.as_query().values() + } + } + + impl<'a, T> $crate::insertable::Insertable for &'a table + where + table: $crate::insertable::Insertable, + { + type Values =
>::Values; + + fn values(self) -> Self::Values { + (*self).values() + } + } + + /// Contains all of the columns of this table + pub mod columns { + use super::table; + $($imports)* + + #[allow(non_camel_case_types, dead_code)] + #[derive(Debug, Clone, Copy, $crate::query_builder::QueryId)] + /// Represents `table_name.*`, which is sometimes needed for + /// efficient count queries. It cannot be used in place of + /// `all_columns`, and has a `SqlType` of `()` to prevent it + /// being used that way + pub struct star; + + impl<__GB> $crate::expression::ValidGrouping<__GB> for star + where + ($($column_name,)+): $crate::expression::ValidGrouping<__GB>, + { + type IsAggregate = <($($column_name,)+) as $crate::expression::ValidGrouping<__GB>>::IsAggregate; + } + + impl $crate::Expression for star { + type SqlType = $crate::expression::expression_types::NotSelectable; + } + + impl $crate::query_builder::QueryFragment for star where +
::FromClause: $crate::query_builder::QueryFragment, + { + #[allow(non_snake_case)] + fn walk_ast(&self, mut __out: $crate::query_builder::AstPass) -> $crate::result::QueryResult<()> { + use $crate::QuerySource; + table.from_clause().walk_ast(__out.reborrow())?; + __out.push_sql(".*"); + Ok(()) + } + } + + impl $crate::SelectableExpression
for star { + } + + impl $crate::AppearsOnTable
for star { + } + + $($crate::__diesel_column! { + table = table, + name = $column_name, + sql_name = $column_sql_name, + ty = ($($column_ty)*), + $($column)* + })+ + + $crate::__diesel_valid_grouping_for_table_columns! { + primary_key = $primary_key, $($column_name,)* + } + } + } + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_valid_grouping_for_table_columns { + (primary_key = ($primary_key: tt), $($cols: ident,)* ) => { + $crate::__diesel_valid_grouping_for_table_columns! { + primary_key = $primary_key, + $($cols,)* + } + }; + (primary_key = $primary_key: tt, $left_col: ident, $($right_col: ident,)+) => { + $( + $crate::static_cond! { + if $left_col == $primary_key { + impl $crate::expression::IsContainedInGroupBy<$right_col> for $left_col { + type Output = $crate::expression::is_contained_in_group_by::Yes; + } + } else { + impl $crate::expression::IsContainedInGroupBy<$right_col> for $left_col { + type Output = $crate::expression::is_contained_in_group_by::No; + } + } + } + + $crate::static_cond! { + if $right_col == $primary_key { + impl $crate::expression::IsContainedInGroupBy<$left_col> for $right_col { + type Output = $crate::expression::is_contained_in_group_by::Yes; + } + } else { + impl $crate::expression::IsContainedInGroupBy<$left_col> for $right_col { + type Output = $crate::expression::is_contained_in_group_by::No; + } + } + } + )* + $crate::__diesel_valid_grouping_for_table_columns! { + primary_key = $primary_key, + $($right_col,)* + } + }; + (primary_key = $primary_key: tt, $left_col: ident,) => {}; + (primary_key = $primary_key: tt) => {}; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_table_query_source_impl { + ($table_struct:ident, public, $table_name:expr) => { + impl $crate::QuerySource for $table_struct { + type FromClause = $crate::query_builder::nodes::Identifier<'static>; + type DefaultSelection = ::AllColumns; + + fn from_clause(&self) -> Self::FromClause { + $crate::query_builder::nodes::Identifier($table_name) + } + + fn default_selection(&self) -> Self::DefaultSelection { + use $crate::Table; + Self::all_columns() + } + } + }; + + ($table_struct:ident, $schema_name:ident, $table_name:expr) => { + impl $crate::QuerySource for $table_struct { + type FromClause = $crate::query_builder::nodes::InfixNode< + 'static, + $crate::query_builder::nodes::Identifier<'static>, + $crate::query_builder::nodes::Identifier<'static>, + >; + type DefaultSelection = ::AllColumns; + + fn from_clause(&self) -> Self::FromClause { + $crate::query_builder::nodes::InfixNode::new( + $crate::query_builder::nodes::Identifier(stringify!($schema_name)), + $crate::query_builder::nodes::Identifier($table_name), + ".", + ) + } + + fn default_selection(&self) -> Self::DefaultSelection { + use $crate::Table; + Self::all_columns() + } + } + }; +} + +/// Allow two tables to be referenced in a join query without providing an +/// explicit `ON` clause. +/// +/// The generated `ON` clause will always join to the primary key of the parent +/// table. This macro removes the need to call [`.on`] explicitly, you will +/// still need to invoke [`allow_tables_to_appear_in_same_query!`] for these two tables to +/// be able to use the resulting query, unless you are using `diesel print-schema` +/// which will generate it for you. +/// +/// If you are using `infer_schema!` or `diesel print-schema`, an invocation of +/// this macro will be generated for every foreign key in your database unless +/// one of the following is true: +/// +/// - The foreign key references something other than the primary key +/// - The foreign key is composite +/// - There is more than one foreign key connecting two tables +/// - The foreign key is self-referential +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// use schema::*; +/// +/// # /* +/// joinable!(posts -> users (user_id)); +/// allow_tables_to_appear_in_same_query!(posts, users); +/// # */ +/// +/// # fn main() { +/// let implicit_on_clause = users::table.inner_join(posts::table); +/// let implicit_on_clause_sql = diesel::debug_query::(&implicit_on_clause).to_string(); +/// +/// let explicit_on_clause = users::table +/// .inner_join(posts::table.on(posts::user_id.eq(users::id))); +/// let explicit_on_clause_sql = diesel::debug_query::(&explicit_on_clause).to_string(); +/// +/// assert_eq!(implicit_on_clause_sql, explicit_on_clause_sql); +/// # } +/// +/// ``` +/// +/// In the example above, the line `joinable!(posts -> users (user_id));` +/// +/// specifies the relation of the tables and the ON clause in the following way: +/// +/// `child_table -> parent_table (foreign_key)` +/// +/// * `parent_table` is the Table with the Primary key. +/// +/// * `child_table` is the Table with the Foreign key. +/// +/// So given the Table decaration from [Associations docs](associations/index.html) +/// +/// * The parent table would be `User` +/// * The child table would be `Post` +/// * and the Foreign key would be `Post.user_id` +/// +/// For joins that do not explicitly use on clauses via [`JoinOnDsl`](prelude/trait.JoinOnDsl.html) +/// the following on clause is generated implicitly: +/// ```sql +/// post JOIN users ON posts.user_id = users.id +/// ``` +#[macro_export] +macro_rules! joinable { + ($($child:ident)::* -> $($parent:ident)::* ($source:ident)) => { + $crate::joinable_inner!($($child)::* ::table => $($parent)::* ::table : ($($child)::* ::$source = $($parent)::* ::table)); + $crate::joinable_inner!($($parent)::* ::table => $($child)::* ::table : ($($child)::* ::$source = $($parent)::* ::table)); + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! joinable_inner { + ($left_table:path => $right_table:path : ($foreign_key:path = $parent_table:path)) => { + $crate::joinable_inner!( + left_table_ty = $left_table, + right_table_ty = $right_table, + right_table_expr = $right_table, + foreign_key = $foreign_key, + primary_key_ty = <$parent_table as $crate::query_source::Table>::PrimaryKey, + primary_key_expr = + <$parent_table as $crate::query_source::Table>::primary_key(&$parent_table), + ); + }; + + ( + left_table_ty = $left_table_ty:ty, + right_table_ty = $right_table_ty:ty, + right_table_expr = $right_table_expr:expr, + foreign_key = $foreign_key:path, + primary_key_ty = $primary_key_ty:ty, + primary_key_expr = $primary_key_expr:expr, + ) => { + impl $crate::JoinTo<$right_table_ty> for $left_table_ty { + type FromClause = $right_table_ty; + type OnClause = $crate::dsl::Eq< + $crate::expression::nullable::Nullable<$foreign_key>, + $crate::expression::nullable::Nullable<$primary_key_ty>, + >; + + fn join_target(rhs: $right_table_ty) -> (Self::FromClause, Self::OnClause) { + use $crate::{ExpressionMethods, NullableExpressionMethods}; + + ( + rhs, + $foreign_key.nullable().eq($primary_key_expr.nullable()), + ) + } + } + }; +} + +/// Allow two or more tables which are otherwise unrelated to be used together +/// in a query. +/// +/// This macro must be invoked any time two tables need to appear in the same +/// query either because they are being joined together, or because one appears +/// in a subselect. When this macro is invoked with more than 2 tables, every +/// combination of those tables will be allowed to appear together. +/// +/// If you are using `diesel print-schema`, an invocation of +/// this macro will be generated for you for all tables in your schema. +/// +/// # Example +/// +/// ```ignore +/// // This would be required to do `users.inner_join(posts.inner_join(comments))` +/// allow_tables_to_appear_in_same_query!(comments, posts, users); +/// ``` +/// +/// When more than two tables are passed, the relevant code is generated for +/// every combination of those tables. This code would be equivalent to the +/// previous example. +/// +/// ```ignore +/// allow_tables_to_appear_in_same_query!(comments, posts); +/// allow_tables_to_appear_in_same_query!(comments, users); +/// allow_tables_to_appear_in_same_query!(posts, users); +/// ``` +#[macro_export] +macro_rules! allow_tables_to_appear_in_same_query { + ($left_mod:ident, $($right_mod:ident),+ $(,)*) => { + $( + impl $crate::query_source::AppearsInFromClause<$left_mod::table> + for $right_mod::table + { + type Count = $crate::query_source::Never; + } + + impl $crate::query_source::AppearsInFromClause<$right_mod::table> + for $left_mod::table + { + type Count = $crate::query_source::Never; + } + )+ + $crate::allow_tables_to_appear_in_same_query!($($right_mod,)+); + }; + + ($last_table:ident,) => {}; + + () => {}; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __diesel_impl_allow_in_same_group_by_clause { + ( + left = [$($left_path:tt)::+], + ) => {}; + ( + left = [$($left_path:tt)::+], + $($right_path:tt)::+ + ) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$($left_path)+], + right = [$($right_path)+], + left_tbl = [], + left_path = [], + } + }; + ( + left = [$($left_path:tt)::+], + $($right_path:tt)::+, + $($other:tt)* + ) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$($left_path)+], + right = [$($right_path)+], + left_tbl = [], + left_path = [], + } + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$($left_path)::+], + $($other)* + } + }; + ( + left = [$left_path_p1: tt $($left_path: tt)+], + right = [$($right_path: tt)*], + left_tbl = [$($left_tbl:tt)?], + left_path = [$($left_out_path:tt)*], + ) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$($left_path)+], + right = [$($right_path)*], + left_tbl = [$left_path_p1], + left_path = [$($left_out_path)* $($left_tbl)?], + } + }; + ( + left = [$left_col: tt], + right = [$($right_path: tt)*], + left_tbl = [$($left_tbl:tt)?], + left_path = [$($left_out_path:tt)*], + ) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$left_col], + right = [$($right_path)*], + left_tbl = [$($left_tbl)?], + left_path = [$($left_out_path)*], + right_tbl = [], + right_path = [], + } + }; + ( + left = [$left_col: tt ], + right = [$right_path_p1: tt $($right_path: tt)+], + left_tbl = [$($left_tbl:tt)?], + left_path = [$($left_out_path:tt)*], + right_tbl = [$($right_tbl:tt)?], + right_path = [$($right_out_path:tt)*], + ) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$left_col], + right = [$($right_path)+], + left_tbl = [$($left_tbl)?], + left_path = [$($left_out_path)*], + right_tbl = [$right_path_p1], + right_path = [$($right_out_path)* $($right_tbl)?], + } + }; + ( + left = [$left_col: tt], + right = [$right_col: tt], + left_tbl = [$left_tbl:tt], + left_path = [$($left_begin:tt)*], + right_tbl = [$right_tbl:tt], + right_path = [$($right_begin:tt)*], + ) => { + $crate::static_cond! { + if $left_tbl != $right_tbl { + impl $crate::expression::IsContainedInGroupBy<$($left_begin ::)* $left_tbl :: $left_col> for $($right_begin ::)* $right_tbl :: $right_col { + type Output = $crate::expression::is_contained_in_group_by::No; + } + + impl $crate::expression::IsContainedInGroupBy<$($right_begin ::)* $right_tbl :: $right_col> for $($left_begin ::)* $left_tbl :: $left_col { + type Output = $crate::expression::is_contained_in_group_by::No; + } + } + } + }; + ( + left = [$left_col: tt], + right = [$right_col: tt], + left_tbl = [$($left_tbl:tt)?], + left_path = [$($left_begin:tt)*], + right_tbl = [$($right_tbl:tt)?], + right_path = [$($right_begin:tt)*], + ) => { + impl $crate::expression::IsContainedInGroupBy<$($left_begin ::)* $($left_tbl ::)? $left_col> for $($right_begin ::)* $($right_tbl ::)? $right_col { + type Output = $crate::expression::is_contained_in_group_by::No; + } + + impl $crate::expression::IsContainedInGroupBy<$($right_begin ::)* $($right_tbl ::)? $right_col> for $($left_begin ::)* $($left_tbl ::)? $left_col { + type Output = $crate::expression::is_contained_in_group_by::No; + } + }; + +} + + +/// Allow two or more columns which are otherwise unrelated to be used together +/// in a group by clause. +/// +/// This macro must be invoked any time two columns need to appear in the same +/// group by clause. When this macro is invoked with more than 2 columns, every +/// combination of those columns will be allowed to appear together. +/// +/// # Example +/// +/// ```ignore +/// // This would be required to do +/// // `users::table.inner_join(posts::table).group_by((users::name, users::hair_color, posts::id))` +/// allow_columns_to_appear_in_same_group_by_clause!(users::name, users::hair_color, posts::id); +/// ``` +/// +/// When more than two columns are passed, the relevant code is generated for +/// every combination of those columns. This code would be equivalent to the +/// previous example. +/// +/// ```ignore +/// allow_columns_to_appear_in_same_group_by_clause!(users::name, users::hair_color); +/// allow_columns_to_appear_in_same_group_by_clause!(users::name, posts::id); +/// allow_columns_to_appear_in_same_group_by_clause!(users::hair_color, posts::id); +/// ``` +#[macro_export] +macro_rules! allow_columns_to_appear_in_same_group_by_clause { + ($($left_path:tt)::+, $($right_path:tt)::+ $(,)?) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$($left_path)::+], + $($right_path)::+, + } + }; + ($($left_path:tt)::+, $($right_path:tt)::+, $($other: tt)*) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$($left_path)::+], + $($right_path)::+, + $($other)* + } + $crate::allow_columns_to_appear_in_same_group_by_clause! { + $($right_path)::+, + $($other)* + } + }; + ($last_col:ty,) => {}; + () => {}; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_with_dollar_sign { + ($($body:tt)*) => { + macro_rules! __with_dollar_sign { $($body)* } + __with_dollar_sign!($); + } +} + + +// The order of these modules is important (at least for those which have tests). +// Utility macros which don't call any others need to come first. +#[macro_use] +mod internal; +#[macro_use] +mod static_cond; +#[macro_use] +mod ops; +#[macro_use] +mod tuples; + +#[cfg(test)] +mod tests { + use crate::prelude::*; + + table! { + foo.bars { + id -> Integer, + baz -> Text, + } + } + + mod my_types { + #[derive(Debug, Clone, Copy, crate::sql_types::SqlType)] + pub struct MyCustomType; + } + + table! { + use crate::sql_types::*; + use crate::macros::tests::my_types::*; + + table_with_custom_types { + id -> Integer, + my_type -> MyCustomType, + } + } + + table! { + use crate::sql_types::*; + use crate::macros::tests::my_types::*; + + /// Table documentation + /// + /// some in detail documentation + table_with_custom_type_and_id (a) { + /// Column documentation + /// + /// some more details + a -> Integer, + my_type -> MyCustomType, + } + } + + #[test] + #[cfg(feature = "postgres")] + fn table_with_custom_schema() { + use crate::pg::Pg; + let expected_sql = r#"SELECT "foo"."bars"."baz" FROM "foo"."bars" -- binds: []"#; + assert_eq!( + expected_sql, + &crate::debug_query::(&bars::table.select(bars::baz)).to_string() + ); + } + + table! { + use crate::sql_types; + use crate::sql_types::*; + + table_with_arbitrarily_complex_types { + id -> sql_types::Integer, + qualified_nullable -> sql_types::Nullable, + deeply_nested_type -> Nullable>, + // This actually should work, but there appears to be a rustc bug + // on the `AsExpression` bound for `EqAll` when the ty param is a projection + // projected_type -> as sql_types::IntoNullable>::Nullable, + //random_tuple -> (Integer, Integer), + } + } + + table!( + foo { + /// Column doc + id -> Integer, + + #[sql_name = "type"] + /// Also important to document this column + mytype -> Integer, + + /// And this one + #[sql_name = "bleh"] + hey -> Integer, + } + ); + + #[test] + #[cfg(feature = "postgres")] + fn table_with_column_renaming_postgres() { + use crate::pg::Pg; + let expected_sql = + r#"SELECT "foo"."id", "foo"."type", "foo"."bleh" FROM "foo" WHERE ("foo"."type" = $1) -- binds: [1]"#; + assert_eq!( + expected_sql, + crate::debug_query::(&foo::table.filter(foo::mytype.eq(1))).to_string() + ); + } + + #[test] + #[cfg(feature = "mysql")] + fn table_with_column_renaming_mysql() { + use crate::mysql::Mysql; + let expected_sql = + r#"SELECT `foo`.`id`, `foo`.`type`, `foo`.`bleh` FROM `foo` WHERE (`foo`.`type` = ?) -- binds: [1]"#; + assert_eq!( + expected_sql, + crate::debug_query::(&foo::table.filter(foo::mytype.eq(1))).to_string() + ); + } + + #[test] + #[cfg(feature = "sqlite")] + fn table_with_column_renaming_sqlite() { + use crate::sqlite::Sqlite; + let expected_sql = + r#"SELECT `foo`.`id`, `foo`.`type`, `foo`.`bleh` FROM `foo` WHERE (`foo`.`type` = ?) -- binds: [1]"#; + assert_eq!( + expected_sql, + crate::debug_query::(&foo::table.filter(foo::mytype.eq(1))).to_string() + ); + } + + table!( + use crate::sql_types::*; + + /// Some documentation + #[sql_name="mod"] + /// Some more documentation + bar { + id -> Integer, + } + ); + + #[test] + #[cfg(feature = "postgres")] + fn table_renaming_postgres() { + use crate::pg::Pg; + let expected_sql = r#"SELECT "mod"."id" FROM "mod" -- binds: []"#; + assert_eq!( + expected_sql, + crate::debug_query::(&bar::table.select(bar::id)).to_string() + ); + } + + #[test] + #[cfg(feature = "mysql")] + fn table_renaming_mysql() { + use crate::mysql::Mysql; + let expected_sql = r#"SELECT `mod`.`id` FROM `mod` -- binds: []"#; + assert_eq!( + expected_sql, + crate::debug_query::(&bar::table.select(bar::id)).to_string() + ); + } + + #[test] + #[cfg(feature = "sqlite")] + fn table_renaming_sqlite() { + use crate::sqlite::Sqlite; + let expected_sql = r#"SELECT `mod`.`id` FROM `mod` -- binds: []"#; + assert_eq!( + expected_sql, + crate::debug_query::(&bar::table.select(bar::id)).to_string() + ); + } + + mod tests_for_allow_combined_group_by_syntax { + table! { + a(b) { + b -> Text, + c -> Text, + d -> Text, + e -> Text, + } + } + + table! { + b(a) { + a -> Text, + c -> Text, + d -> Text, + } + } + + table! { + c(a) { + a -> Text, + b -> Text, + d -> Text, + } + } + + // allow using table::collumn + allow_columns_to_appear_in_same_group_by_clause!( + a::b, b::a, a::d, + ); + + // allow using full paths + allow_columns_to_appear_in_same_group_by_clause!( + self::a::c, self::b::c, self::b::d, + ); + + use self::a::d as a_d; + use self::b::d as b_d; + use self::c::d as c_d; + + // allow using plain identifiers + allow_columns_to_appear_in_same_group_by_clause!( + a_d, b_d, c_d + ); + + // allow mixing all variants + allow_columns_to_appear_in_same_group_by_clause!( + c_d, self::b::a, a::e, + ); + } +} diff --git a/collector/benchmarks/diesel/diesel/src/macros/ops.rs b/collector/benchmarks/diesel/diesel/src/macros/ops.rs new file mode 100644 index 000000000..dac4fde71 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/macros/ops.rs @@ -0,0 +1,106 @@ +#[macro_export] +/// Implements the Rust operator for a given type. If you create a new SQL +/// function, which returns a type that you'd like to use an operator on, you +/// should invoke this macro. Unfortunately, Rust disallows us from +/// automatically implementing `Add` and other traits from `std::ops`, under its +/// orphan rules. +macro_rules! operator_allowed { + ($tpe:ty, $op:ident, $fn_name:ident) => { + impl ::std::ops::$op for $tpe + where + Rhs: $crate::expression::AsExpression< + <<$tpe as $crate::Expression>::SqlType as $crate::sql_types::ops::$op>::Rhs, + >, + { + type Output = $crate::expression::ops::$op; + + fn $fn_name(self, rhs: Rhs) -> Self::Output { + $crate::expression::ops::$op::new(self, rhs.as_expression()) + } + } + }; +} + +#[macro_export] +/// Indicates that an expression allows all numeric operators. If you create new +/// SQL functions that return a numeric type, you should invoke this macro that +/// type. Unfortunately, Rust disallows us from automatically implementing `Add` +/// for types which implement `Expression`, under its orphan rules. +macro_rules! numeric_expr { + ($tpe:ty) => { + $crate::operator_allowed!($tpe, Add, add); + $crate::operator_allowed!($tpe, Sub, sub); + $crate::operator_allowed!($tpe, Div, div); + $crate::operator_allowed!($tpe, Mul, mul); + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_generate_ops_impls_if_numeric { + ($column_name:ident, Nullable<$($inner:tt)::*>) => { $crate::__diesel_generate_ops_impls_if_numeric!($column_name, $($inner)::*); }; + + ($column_name:ident, Unsigned<$($inner:tt)::*>) => { $crate::__diesel_generate_ops_impls_if_numeric!($column_name, $($inner)::*); }; + + ($column_name:ident, SmallInt) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Int2) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Smallint) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, SmallSerial) => { $crate::numeric_expr!($column_name); }; + + ($column_name:ident, Integer) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Int4) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Serial) => { $crate::numeric_expr!($column_name); }; + + ($column_name:ident, BigInt) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Int8) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Bigint) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, BigSerial) => { $crate::numeric_expr!($column_name); }; + + ($column_name:ident, Float) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Float4) => { $crate::numeric_expr!($column_name); }; + + ($column_name:ident, Double) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Float8) => { $crate::numeric_expr!($column_name); }; + + ($column_name:ident, Numeric) => { $crate::numeric_expr!($column_name); }; + + ($column_name:ident, $non_numeric_type:ty) => {}; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! date_time_expr { + ($tpe:ty) => { + $crate::operator_allowed!($tpe, Add, add); + $crate::operator_allowed!($tpe, Sub, sub); + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_generate_ops_impls_if_date_time { + ($column_name:ident, Nullable<$($inner:tt)::*>) => { $crate::__diesel_generate_ops_impls_if_date_time!($column_name, $($inner)::*); }; + ($column_name:ident, Time) => { $crate::date_time_expr!($column_name); }; + ($column_name:ident, Date) => { $crate::date_time_expr!($column_name); }; + ($column_name:ident, Timestamp) => { $crate::date_time_expr!($column_name); }; + ($column_name:ident, Timestamptz) => { $crate::date_time_expr!($column_name); }; + ($column_name:ident, $non_date_time_type:ty) => {}; +} + +#[macro_export(local_inner_macros)] +#[doc(hidden)] +macro_rules! network_expr { + ($tpe:ty) => { + operator_allowed!($tpe, Add, add); + operator_allowed!($tpe, Sub, sub); + }; +} + +#[macro_export(local_inner_macros)] +#[doc(hidden)] +macro_rules! __diesel_generate_ops_impls_if_network { + ($column_name:ident, Nullable<$($inner:tt)::*>) => { __diesel_generate_ops_impls_if_network!($column_name, $($inner)::*); }; + ($column_name:ident, Cidr) => { network_expr!($column_name); }; + ($column_name:ident, Inet) => { network_expr!($column_name); }; + ($column_name:ident, $non_network_type:ty) => {}; +} diff --git a/collector/benchmarks/diesel/diesel/src/macros/static_cond.rs b/collector/benchmarks/diesel/diesel/src/macros/static_cond.rs new file mode 100644 index 000000000..d59c78ee2 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/macros/static_cond.rs @@ -0,0 +1,38 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] // https://github.com/rust-lang-nursery/rustfmt/issues/2754 + +// Vendored from the static-cond crate as macro re-exports are not available in stable Rust. +// https://github.com/durka/static-cond/blob/36aa2dd/src/lib.rs +// +// Code is dual licensed under MIT/Apache-2.0 +// Copyright (c) 2016 Alex Burka +#[macro_export] +#[doc(hidden)] +macro_rules! static_cond { + // private rule to define and call the local macro + (@go $lhs:tt $rhs:tt $arm1:tt $arm2:tt) => { + // note that the inner macro has no captures (it can't, because there's no way to escape `$`) + macro_rules! __static_cond { + ($lhs $lhs) => $arm1; + ($lhs $rhs) => $arm2 + } + + __static_cond!($lhs $rhs); + }; + + // no else condition provided: fall through with empty else + (if $lhs:tt == $rhs:tt $then:tt) => { + $crate::static_cond!(if $lhs == $rhs $then else { }); + }; + (if $lhs:tt != $rhs:tt $then:tt) => { + $crate::static_cond!(if $lhs != $rhs $then else { }); + }; + + // we evaluate a conditional by generating a new macro (in an inner scope, so name shadowing is + // not a big concern) and calling it + (if $lhs:tt == $rhs:tt $then:tt else $els:tt) => { + $crate::static_cond!(@go $lhs $rhs $then $els); + }; + (if $lhs:tt != $rhs:tt $then:tt else $els:tt) => { + $crate::static_cond!(@go $lhs $rhs $els $then); + }; +} diff --git a/collector/benchmarks/diesel/diesel/src/macros/tuples.rs b/collector/benchmarks/diesel/diesel/src/macros/tuples.rs new file mode 100644 index 000000000..8a83e32a0 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/macros/tuples.rs @@ -0,0 +1,8532 @@ +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_for_each_tuple { + ($callback:ident) => { + $callback! { + 1 { + (0) -> A, SA, TA, + } + 2 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + } + 3 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + } + 4 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + } + 5 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + } + 6 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + } + 7 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + } + 8 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + } + 9 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + } + 10 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + } + 11 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + } + 12 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + } + 13 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + } + 14 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + } + 15 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + } + 16 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + } + } + + #[cfg(feature = "32-column-tables")] + $callback! { + 17 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + } + 18 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + } + 19 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + } + 20 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + } + 21 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + } + 22 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + } + 23 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + } + 24 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + } + 25 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + } + 26 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + } + 27 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + } + 28 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + } + 29 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + } + 30 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + } + 31 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + } + 32 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + } + } + + #[cfg(feature = "64-column-tables")] + $callback! { + 33 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + } + 34 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + } + 35 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + } + 36 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + } + 37 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + } + 38 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + } + 39 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + } + 40 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + } + 41 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + } + 42 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + } + 43 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + } + 44 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + } + 45 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + } + 46 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + } + 47 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + } + 48 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + } + 49 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + } + 50 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + } + 51 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + } + 52 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + } + 53 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + } + 54 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + } + 55 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + } + 56 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + } + 57 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + } + 58 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + } + 59 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + } + 60 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + } + 61 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + } + 62 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + } + 63 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + } + 64 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + } + } + + #[cfg(feature = "128-column-tables")] + $callback! { + 65 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + } + 66 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + } + 67 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + } + 68 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + } + 69 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + } + 70 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + } + 71 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + } + 72 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + } + 73 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + } + 74 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + } + 75 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + } + 76 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + } + 77 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + } + 78 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + } + 79 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + } + 80 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + } + 81 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + } + 82 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + } + 83 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + } + 84 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + } + 85 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + } + 86 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + } + 87 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + } + 88 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + } + 89 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + } + 90 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + } + 91 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + } + 92 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + } + 93 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + } + 94 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + } + 95 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + } + 96 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + } + 97 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + } + 98 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + } + 99 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + } + 100 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + } + 101 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + } + 102 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + } + 103 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + } + 104 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + } + 105 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + } + 106 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + } + 107 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + } + 108 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + } + 109 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + } + 110 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + } + 111 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + } + 112 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + } + 113 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + } + 114 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + } + 115 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + } + 116 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + } + 117 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + } + 118 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + } + 119 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + } + 120 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + (119) -> DP, SDP, TDP, + } + 121 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + (119) -> DP, SDP, TDP, + (120) -> DQ, SDQ, TDQ, + } + 122 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + (119) -> DP, SDP, TDP, + (120) -> DQ, SDQ, TDQ, + (121) -> DR, SDR, TDR, + } + 123 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + (119) -> DP, SDP, TDP, + (120) -> DQ, SDQ, TDQ, + (121) -> DR, SDR, TDR, + (122) -> DS, SDS, TDS, + } + 124 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + (119) -> DP, SDP, TDP, + (120) -> DQ, SDQ, TDQ, + (121) -> DR, SDR, TDR, + (122) -> DS, SDS, TDS, + (123) -> DT, SDT, TDT, + } + 125 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + (119) -> DP, SDP, TDP, + (120) -> DQ, SDQ, TDQ, + (121) -> DR, SDR, TDR, + (122) -> DS, SDS, TDS, + (123) -> DT, SDT, TDT, + (124) -> DU, SDU, TDU, + } + 126 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + (119) -> DP, SDP, TDP, + (120) -> DQ, SDQ, TDQ, + (121) -> DR, SDR, TDR, + (122) -> DS, SDS, TDS, + (123) -> DT, SDT, TDT, + (124) -> DU, SDU, TDU, + (125) -> DV, SDV, TDV, + } + 127 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + (119) -> DP, SDP, TDP, + (120) -> DQ, SDQ, TDQ, + (121) -> DR, SDR, TDR, + (122) -> DS, SDS, TDS, + (123) -> DT, SDT, TDT, + (124) -> DU, SDU, TDU, + (125) -> DV, SDV, TDV, + (126) -> DW, SDW, TDW, + } + 128 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + (119) -> DP, SDP, TDP, + (120) -> DQ, SDQ, TDQ, + (121) -> DR, SDR, TDR, + (122) -> DS, SDS, TDS, + (123) -> DT, SDT, TDT, + (124) -> DU, SDU, TDU, + (125) -> DV, SDV, TDV, + (126) -> DW, SDW, TDW, + (127) -> DX, SDX, TDX, + } + } + }; +} diff --git a/collector/benchmarks/diesel/diesel/src/migration/errors.rs b/collector/benchmarks/diesel/diesel/src/migration/errors.rs new file mode 100644 index 000000000..c0983a71e --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/migration/errors.rs @@ -0,0 +1,121 @@ +//! Error types that represent migration errors. +//! These are split into multiple segments, depending on +//! where in the migration process an error occurs. + +use std::convert::From; +use std::error::Error; +use std::path::PathBuf; +use std::{fmt, io}; + +use crate::result; + +/// Errors that occur while preparing to run migrations +#[derive(Debug)] +#[non_exhaustive] +pub enum MigrationError { + /// The migration directory wasn't found + MigrationDirectoryNotFound(PathBuf), + /// Provided migration was in an unknown format + UnknownMigrationFormat(PathBuf), + /// General system IO error + IoError(io::Error), + /// Provided migration had an incompatible version number + UnknownMigrationVersion(String), + /// No migrations had to be/ could be run + NoMigrationRun, +} + +impl Error for MigrationError {} + +impl fmt::Display for MigrationError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + MigrationError::MigrationDirectoryNotFound(ref p) => write!( + f, + "Unable to find migrations directory in {:?} or any parent directories.", + p + ), + MigrationError::UnknownMigrationFormat(_) => write!( + f, + "Invalid migration directory, the directory's name should be \ + _, and it should only contain up.sql and down.sql." + ), + MigrationError::IoError(ref error) => write!(f, "{}", error), + MigrationError::UnknownMigrationVersion(_) => write!( + f, + "Unable to find migration version to revert in the migrations directory." + ), + MigrationError::NoMigrationRun => write!( + f, + "No migrations have been run. Did you forget `diesel migration run`?" + ), + } + } +} + +impl PartialEq for MigrationError { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + ( + &MigrationError::MigrationDirectoryNotFound(_), + &MigrationError::MigrationDirectoryNotFound(_), + ) => true, + ( + &MigrationError::UnknownMigrationFormat(ref p1), + &MigrationError::UnknownMigrationFormat(ref p2), + ) => p1 == p2, + _ => false, + } + } +} + +impl From for MigrationError { + fn from(e: io::Error) -> Self { + MigrationError::IoError(e) + } +} + +/// Errors that occur while running migrations +#[derive(Debug, PartialEq)] +#[allow(clippy::enum_variant_names)] +#[non_exhaustive] +pub enum RunMigrationsError { + /// A general migration error occured + MigrationError(MigrationError), + /// The provided migration included an invalid query + QueryError(result::Error), + /// The provided migration was empty + EmptyMigration, +} + +impl Error for RunMigrationsError {} + +impl fmt::Display for RunMigrationsError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + RunMigrationsError::MigrationError(ref error) => write!(f, "Failed with: {}", error), + RunMigrationsError::QueryError(ref error) => write!(f, "Failed with: {}", error), + RunMigrationsError::EmptyMigration => { + write!(f, "Failed with: Attempted to run an empty migration.") + } + } + } +} + +impl From for RunMigrationsError { + fn from(e: MigrationError) -> Self { + RunMigrationsError::MigrationError(e) + } +} + +impl From for RunMigrationsError { + fn from(e: result::Error) -> Self { + RunMigrationsError::QueryError(e) + } +} + +impl From for RunMigrationsError { + fn from(e: io::Error) -> Self { + RunMigrationsError::MigrationError(e.into()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/migration/mod.rs b/collector/benchmarks/diesel/diesel/src/migration/mod.rs new file mode 100644 index 000000000..a37068d03 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/migration/mod.rs @@ -0,0 +1,102 @@ +//! Representation of migrations + +mod errors; +pub use self::errors::{MigrationError, RunMigrationsError}; + +use crate::connection::{Connection, SimpleConnection}; +use crate::result::QueryResult; +use std::path::Path; + +/// Represents a migration that interacts with diesel +pub trait Migration { + /// Get the migration version + fn version(&self) -> &str; + /// Apply this migration + fn run(&self, conn: &dyn SimpleConnection) -> Result<(), RunMigrationsError>; + /// Revert this migration + fn revert(&self, conn: &dyn SimpleConnection) -> Result<(), RunMigrationsError>; + /// Get the migration file path + fn file_path(&self) -> Option<&Path> { + None + } +} + +impl<'a> Migration for Box { + fn version(&self) -> &str { + (&**self).version() + } + + fn run(&self, conn: &dyn SimpleConnection) -> Result<(), RunMigrationsError> { + (&**self).run(conn) + } + + fn revert(&self, conn: &dyn SimpleConnection) -> Result<(), RunMigrationsError> { + (&**self).revert(conn) + } + fn file_path(&self) -> Option<&Path> { + (&**self).file_path() + } +} + +impl<'a> Migration for &'a dyn Migration { + fn version(&self) -> &str { + (&**self).version() + } + + fn run(&self, conn: &dyn SimpleConnection) -> Result<(), RunMigrationsError> { + (&**self).run(conn) + } + + fn revert(&self, conn: &dyn SimpleConnection) -> Result<(), RunMigrationsError> { + (&**self).revert(conn) + } + fn file_path(&self) -> Option<&Path> { + (&**self).file_path() + } +} + +/// Create table statement for the `__diesel_schema_migrations` used +/// used by the postgresql, sqlite and mysql backend +pub const CREATE_MIGRATIONS_TABLE: &str = include_str!("setup_migration_table.sql"); + +/// A trait indicating that a connection could be used to manage migrations +/// +/// Only custom backend implementations need to think about this trait +pub trait MigrationConnection: Connection { + /// Setup the following table: + /// + /// ```rust + /// diesel::table! { + /// __diesel_schema_migrations(version) { + /// version -> Text, + /// /// defaults to `CURRENT_TIMESTAMP` + /// run_on -> Timestamp, + /// } + /// } + /// ``` + fn setup(&self) -> QueryResult; +} + +#[cfg(feature = "postgres")] +impl MigrationConnection for crate::pg::PgConnection { + fn setup(&self) -> QueryResult { + use crate::RunQueryDsl; + crate::sql_query(CREATE_MIGRATIONS_TABLE).execute(self) + } +} + +#[cfg(feature = "mysql")] +impl MigrationConnection for crate::mysql::MysqlConnection { + fn setup(&self) -> QueryResult { + use crate::RunQueryDsl; + crate::sql_query(CREATE_MIGRATIONS_TABLE).execute(self) + } +} + +#[cfg(feature = "sqlite")] +impl MigrationConnection for crate::sqlite::SqliteConnection { + fn setup(&self) -> QueryResult { + use crate::RunQueryDsl; + crate::sql_query(CREATE_MIGRATIONS_TABLE).execute(self) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/migration/setup_migration_table.sql b/collector/benchmarks/diesel/diesel/src/migration/setup_migration_table.sql new file mode 100644 index 000000000..6e2594703 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/migration/setup_migration_table.sql @@ -0,0 +1,4 @@ +CREATE TABLE IF NOT EXISTS __diesel_schema_migrations ( + version VARCHAR(50) PRIMARY KEY NOT NULL, + run_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); diff --git a/collector/benchmarks/diesel/diesel/src/mysql/backend.rs b/collector/benchmarks/diesel/diesel/src/mysql/backend.rs new file mode 100644 index 000000000..39482b871 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/backend.rs @@ -0,0 +1,81 @@ +//! The MySQL backend + +use byteorder::NativeEndian; + +use super::query_builder::MysqlQueryBuilder; +use super::MysqlValue; +use crate::backend::*; +use crate::query_builder::bind_collector::RawBytesBindCollector; +use crate::sql_types::TypeMetadata; + +/// The MySQL backend +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct Mysql; + +#[allow(missing_debug_implementations)] +/// Represents possible types, that can be transmitted as via the +/// Mysql wire protocol +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +pub enum MysqlType { + /// A 8 bit signed integer + Tiny, + /// A 8 bit unsigned integer + UnsignedTiny, + /// A 16 bit signed integer + Short, + /// A 16 bit unsigned integer + UnsignedShort, + /// A 32 bit signed integer + Long, + /// A 32 bit unsigned integer + UnsignedLong, + /// A 64 bit signed integer + LongLong, + /// A 64 bit unsigned integer + UnsignedLongLong, + /// A 32 bit floating point number + Float, + /// A 64 bit floating point number + Double, + /// A fixed point decimal value + Numeric, + /// A datatype to store a time value + Time, + /// A datatype to store a date value + Date, + /// A datatype containing timestamp values ranging from + /// '1000-01-01 00:00:00' to '9999-12-31 23:59:59'. + DateTime, + /// A datatype containing timestamp values ranging from + /// 1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC. + Timestamp, + /// A datatype for string values + String, + /// A datatype containing binary large objects + Blob, + /// A value containing a set of bit's + Bit, + /// A user defined set type + Set, + /// A user defined enum type + Enum, +} + +impl Backend for Mysql { + type QueryBuilder = MysqlQueryBuilder; + type BindCollector = RawBytesBindCollector; + type ByteOrder = NativeEndian; +} + +impl<'a> HasRawValue<'a> for Mysql { + type RawValue = MysqlValue<'a>; +} + +impl TypeMetadata for Mysql { + type TypeMetadata = MysqlType; + type MetadataLookup = (); +} + +impl SupportsDefaultKeyword for Mysql {} +impl UsesAnsiSavepointSyntax for Mysql {} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/connection/bind.rs b/collector/benchmarks/diesel/diesel/src/mysql/connection/bind.rs new file mode 100644 index 000000000..c226f82f2 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/connection/bind.rs @@ -0,0 +1,1438 @@ +use mysqlclient_sys as ffi; +use std::mem; +use std::ops::Index; +use std::os::raw as libc; + +use super::stmt::Statement; +use crate::mysql::connection::stmt::StatementMetadata; +use crate::mysql::types::MYSQL_TIME; +use crate::mysql::{MysqlType, MysqlValue}; +use crate::result::QueryResult; + +pub struct Binds { + data: Vec, +} + +impl Binds { + pub fn from_input_data(input: Iter) -> QueryResult + where + Iter: IntoIterator>)>, + { + let data = input + .into_iter() + .map(BindData::for_input) + .collect::>(); + + Ok(Binds { data }) + } + + pub fn from_output_types(types: Vec>, metadata: &StatementMetadata) -> Self { + let data = metadata + .fields() + .iter() + .zip(types.into_iter().chain(std::iter::repeat(None))) + .map(|(field, tpe)| { + if let Some(tpe) = tpe { + BindData::for_output(tpe.into()) + } else { + BindData::for_output((field.field_type(), field.flags())) + } + }) + .collect(); + + Binds { data } + } + + pub fn with_mysql_binds(&mut self, f: F) -> T + where + F: FnOnce(*mut ffi::MYSQL_BIND) -> T, + { + let mut binds = self + .data + .iter_mut() + .map(|x| unsafe { x.mysql_bind() }) + .collect::>(); + f(binds.as_mut_ptr()) + } + + pub fn populate_dynamic_buffers(&mut self, stmt: &Statement) -> QueryResult<()> { + for (i, data) in self.data.iter_mut().enumerate() { + data.did_numeric_overflow_occur()?; + // This is safe because we are re-binding the invalidated buffers + // at the end of this function + unsafe { + if let Some((mut bind, offset)) = data.bind_for_truncated_data() { + stmt.fetch_column(&mut bind, i, offset)? + } else { + data.update_buffer_length() + } + } + } + + unsafe { self.with_mysql_binds(|bind_ptr| stmt.bind_result(bind_ptr)) } + } + + pub fn update_buffer_lengths(&mut self) { + for data in &mut self.data { + data.update_buffer_length(); + } + } + + pub fn len(&self) -> usize { + self.data.len() + } +} + +impl Index for Binds { + type Output = BindData; + fn index(&self, index: usize) -> &Self::Output { + &self.data[index] + } +} + +bitflags::bitflags! { + pub(crate) struct Flags: u32 { + const NOT_NULL_FLAG = 1; + const PRI_KEY_FAG = 2; + const UNIQUE_KEY_FLAG = 4; + const MULTIPLE_KEY_FLAG = 8; + const BLOB_FLAG = 16; + const UNSIGNED_FLAG = 32; + const ZEROFILL_FLAG = 64; + const BINARY_FLAG = 128; + const ENUM_FLAG = 256; + const AUTO_INCREMENT_FLAG = 512; + const TIMESTAMP_FLAG = 1024; + const SET_FLAG = 2048; + const NO_DEFAULT_VALUE_FLAG = 4096; + const ON_UPDATE_NOW_FLAG = 8192; + const NUM_FLAG = 32768; + const PART_KEY_FLAG = 16384; + const GROUP_FLAG = 32768; + const UNIQUE_FLAG = 65536; + const BINCMP_FLAG = 130_172; + const GET_FIXED_FIELDS_FLAG = (1<<18); + const FIELD_IN_PART_FUNC_FLAG = (1 << 19); + } +} + +impl From for Flags { + fn from(flags: u32) -> Self { + Flags::from_bits(flags).expect( + "We encountered a unknown type flag while parsing \ + Mysql's type information. If you see this error message \ + please open an issue at diesels github page.", + ) + } +} + +#[derive(Debug)] +pub struct BindData { + tpe: ffi::enum_field_types, + bytes: Vec, + length: libc::c_ulong, + flags: Flags, + is_null: ffi::my_bool, + is_truncated: Option, +} + +impl BindData { + fn for_input((tpe, data): (MysqlType, Option>)) -> Self { + let is_null = if data.is_none() { 1 } else { 0 }; + let bytes = data.unwrap_or_default(); + let length = bytes.len() as libc::c_ulong; + let (tpe, flags) = tpe.into(); + BindData { + tpe, + bytes, + length, + is_null, + is_truncated: None, + flags, + } + } + + fn for_output((tpe, flags): (ffi::enum_field_types, Flags)) -> Self { + let bytes = known_buffer_size_for_ffi_type(tpe) + .map(|len| vec![0; len]) + .unwrap_or_default(); + let length = bytes.len() as libc::c_ulong; + + BindData { + tpe, + bytes, + length, + is_null: 0, + is_truncated: Some(0), + flags, + } + } + + fn is_truncated(&self) -> bool { + self.is_truncated.unwrap_or(0) != 0 + } + + fn is_fixed_size_buffer(&self) -> bool { + known_buffer_size_for_ffi_type(self.tpe).is_some() + } + + pub fn value(&'_ self) -> Option> { + if self.is_null() { + None + } else { + let tpe = (self.tpe, self.flags).into(); + Some(MysqlValue::new(&self.bytes, tpe)) + } + } + + pub fn is_null(&self) -> bool { + self.is_null != 0 + } + + fn update_buffer_length(&mut self) { + use std::cmp::min; + + let actual_bytes_in_buffer = min(self.bytes.capacity(), self.length as usize); + unsafe { self.bytes.set_len(actual_bytes_in_buffer) } + } + + unsafe fn mysql_bind(&mut self) -> ffi::MYSQL_BIND { + let mut bind: ffi::MYSQL_BIND = mem::zeroed(); + bind.buffer_type = self.tpe; + bind.buffer = self.bytes.as_mut_ptr() as *mut libc::c_void; + bind.buffer_length = self.bytes.capacity() as libc::c_ulong; + bind.length = &mut self.length; + bind.is_null = &mut self.is_null; + bind.is_unsigned = self.flags.contains(Flags::UNSIGNED_FLAG) as ffi::my_bool; + + if let Some(ref mut is_truncated) = self.is_truncated { + bind.error = is_truncated; + } + + bind + } + + /// Resizes the byte buffer to fit the value of `self.length`, and returns + /// a tuple of a bind pointing at the truncated data, and the offset to use + /// in order to read the truncated data into it. + /// + /// This invalidates the bind previously returned by `mysql_bind`. Calling + /// this function is unsafe unless the binds are immediately rebound. + unsafe fn bind_for_truncated_data(&mut self) -> Option<(ffi::MYSQL_BIND, usize)> { + if self.is_truncated() { + let offset = self.bytes.capacity(); + let truncated_amount = self.length as usize - offset; + + debug_assert!( + truncated_amount > 0, + "output buffers were invalidated \ + without calling `mysql_stmt_bind_result`" + ); + self.bytes.set_len(offset); + self.bytes.reserve(truncated_amount); + self.bytes.set_len(self.length as usize); + + let mut bind = self.mysql_bind(); + bind.buffer = self.bytes[offset..].as_mut_ptr() as *mut libc::c_void; + bind.buffer_length = truncated_amount as libc::c_ulong; + Some((bind, offset)) + } else { + None + } + } + + fn did_numeric_overflow_occur(&self) -> QueryResult<()> { + use crate::result::Error::DeserializationError; + + if self.is_truncated() && self.is_fixed_size_buffer() { + Err(DeserializationError( + "Numeric overflow/underflow occurred".into(), + )) + } else { + Ok(()) + } + } +} + +impl From for (ffi::enum_field_types, Flags) { + fn from(tpe: MysqlType) -> Self { + use self::ffi::enum_field_types::*; + let mut flags = Flags::empty(); + let tpe = match tpe { + MysqlType::Tiny => MYSQL_TYPE_TINY, + MysqlType::Short => MYSQL_TYPE_SHORT, + MysqlType::Long => MYSQL_TYPE_LONG, + MysqlType::LongLong => MYSQL_TYPE_LONGLONG, + MysqlType::Float => MYSQL_TYPE_FLOAT, + MysqlType::Double => MYSQL_TYPE_DOUBLE, + MysqlType::Time => MYSQL_TYPE_TIME, + MysqlType::Date => MYSQL_TYPE_DATE, + MysqlType::DateTime => MYSQL_TYPE_DATETIME, + MysqlType::Timestamp => MYSQL_TYPE_TIMESTAMP, + MysqlType::String => MYSQL_TYPE_STRING, + MysqlType::Blob => MYSQL_TYPE_BLOB, + MysqlType::Numeric => MYSQL_TYPE_NEWDECIMAL, + MysqlType::Bit => MYSQL_TYPE_BIT, + MysqlType::UnsignedTiny => { + flags = Flags::UNSIGNED_FLAG; + MYSQL_TYPE_TINY + } + MysqlType::UnsignedShort => { + flags = Flags::UNSIGNED_FLAG; + MYSQL_TYPE_SHORT + } + MysqlType::UnsignedLong => { + flags = Flags::UNSIGNED_FLAG; + MYSQL_TYPE_LONG + } + MysqlType::UnsignedLongLong => { + flags = Flags::UNSIGNED_FLAG; + MYSQL_TYPE_LONGLONG + } + MysqlType::Set => { + flags = Flags::SET_FLAG; + MYSQL_TYPE_STRING + } + MysqlType::Enum => { + flags = Flags::ENUM_FLAG; + MYSQL_TYPE_STRING + } + }; + (tpe, flags) + } +} + +impl From<(ffi::enum_field_types, Flags)> for MysqlType { + fn from((tpe, flags): (ffi::enum_field_types, Flags)) -> Self { + use self::ffi::enum_field_types::*; + + let is_unsigned = flags.contains(Flags::UNSIGNED_FLAG); + + // https://docs.oracle.com/cd/E17952_01/mysql-8.0-en/c-api-data-structures.html + // https://dev.mysql.com/doc/dev/mysql-server/8.0.12/binary__log__types_8h.html + // https://dev.mysql.com/doc/internals/en/binary-protocol-value.html + // https://mariadb.com/kb/en/packet_bindata/ + match tpe { + MYSQL_TYPE_TINY if is_unsigned => MysqlType::UnsignedTiny, + MYSQL_TYPE_YEAR | MYSQL_TYPE_SHORT if is_unsigned => MysqlType::UnsignedShort, + MYSQL_TYPE_INT24 | MYSQL_TYPE_LONG if is_unsigned => MysqlType::UnsignedLong, + MYSQL_TYPE_LONGLONG if is_unsigned => MysqlType::UnsignedLongLong, + MYSQL_TYPE_TINY => MysqlType::Tiny, + MYSQL_TYPE_SHORT => MysqlType::Short, + MYSQL_TYPE_INT24 | MYSQL_TYPE_LONG => MysqlType::Long, + MYSQL_TYPE_LONGLONG => MysqlType::LongLong, + MYSQL_TYPE_FLOAT => MysqlType::Float, + MYSQL_TYPE_DOUBLE => MysqlType::Double, + MYSQL_TYPE_DECIMAL | MYSQL_TYPE_NEWDECIMAL => MysqlType::Numeric, + MYSQL_TYPE_BIT => MysqlType::Bit, + + MYSQL_TYPE_TIME => MysqlType::Time, + MYSQL_TYPE_DATE => MysqlType::Date, + MYSQL_TYPE_DATETIME => MysqlType::DateTime, + MYSQL_TYPE_TIMESTAMP => MysqlType::Timestamp, + // Treat json as string because even mysql 8.0 + // throws errors sometimes if we use json for json + MYSQL_TYPE_JSON => MysqlType::String, + + // The documentation states that + // MYSQL_TYPE_STRING is used for enums and sets + // but experimentation has shown that + // just any string like type works, so + // better be safe here + MYSQL_TYPE_BLOB + | MYSQL_TYPE_TINY_BLOB + | MYSQL_TYPE_MEDIUM_BLOB + | MYSQL_TYPE_LONG_BLOB + | MYSQL_TYPE_VAR_STRING + | MYSQL_TYPE_STRING + if flags.contains(Flags::ENUM_FLAG) => + { + MysqlType::Enum + } + MYSQL_TYPE_BLOB + | MYSQL_TYPE_TINY_BLOB + | MYSQL_TYPE_MEDIUM_BLOB + | MYSQL_TYPE_LONG_BLOB + | MYSQL_TYPE_VAR_STRING + | MYSQL_TYPE_STRING + if flags.contains(Flags::SET_FLAG) => + { + MysqlType::Set + } + + // "blobs" may contain binary data + // also "strings" can contain binary data + // but all only if the binary flag is set + // (see the check_all_the_types test case) + MYSQL_TYPE_BLOB + | MYSQL_TYPE_TINY_BLOB + | MYSQL_TYPE_MEDIUM_BLOB + | MYSQL_TYPE_LONG_BLOB + | MYSQL_TYPE_VAR_STRING + | MYSQL_TYPE_STRING + if flags.contains(Flags::BINARY_FLAG) => + { + MysqlType::Blob + } + + // If the binary flag is not set consider everything as string + MYSQL_TYPE_BLOB + | MYSQL_TYPE_TINY_BLOB + | MYSQL_TYPE_MEDIUM_BLOB + | MYSQL_TYPE_LONG_BLOB + | MYSQL_TYPE_VAR_STRING + | MYSQL_TYPE_STRING => MysqlType::String, + + // unsigned seems to be set for year in any case + MYSQL_TYPE_YEAR => unreachable!( + "The year type should have set the unsigned flag. If you ever \ + see this error message, something has gone very wrong. Please \ + open an issue at the diesel githup repo in this case" + ), + // Null value + MYSQL_TYPE_NULL => unreachable!( + "We ensure at the call side that we do not hit this type here. \ + If you ever see this error, something has gone very wrong. \ + Please open an issue at the diesel github repo in this case" + ), + // Those exist in libmysqlclient + // but are just not supported + // + MYSQL_TYPE_VARCHAR | MYSQL_TYPE_ENUM | MYSQL_TYPE_SET | MYSQL_TYPE_GEOMETRY => { + unimplemented!( + "Hit a type that should be unsupported in libmysqlclient. If \ + you ever see this error, they probably have added support for \ + one of those types. Please open an issue at the diesel github \ + repo in this case." + ) + } + + MYSQL_TYPE_NEWDATE + | MYSQL_TYPE_TIME2 + | MYSQL_TYPE_DATETIME2 + | MYSQL_TYPE_TIMESTAMP2 => unreachable!( + "The mysql documentation states that this types are \ + only used on server side, so if you see this error \ + something has gone wrong. Please open a issue at \ + the diesel github repo." + ), + } + } +} + +fn known_buffer_size_for_ffi_type(tpe: ffi::enum_field_types) -> Option { + use self::ffi::enum_field_types as t; + use std::mem::size_of; + + match tpe { + t::MYSQL_TYPE_TINY => Some(1), + t::MYSQL_TYPE_YEAR | t::MYSQL_TYPE_SHORT => Some(2), + t::MYSQL_TYPE_INT24 | t::MYSQL_TYPE_LONG | t::MYSQL_TYPE_FLOAT => Some(4), + t::MYSQL_TYPE_LONGLONG | t::MYSQL_TYPE_DOUBLE => Some(8), + t::MYSQL_TYPE_TIME + | t::MYSQL_TYPE_DATE + | t::MYSQL_TYPE_DATETIME + | t::MYSQL_TYPE_TIMESTAMP => Some(size_of::()), + _ => None, + } +} + +#[cfg(test)] +mod tests { + #[cfg(feature = "bigdecimal")] + use bigdecimal::FromPrimitive; + + use super::MysqlValue; + use super::*; + use crate::deserialize::FromSql; + use crate::prelude::*; + use crate::sql_types::*; + + fn to_value( + bind: &BindData, + ) -> Result> + where + T: FromSql + std::fmt::Debug, + { + let meta = (bind.tpe, bind.flags).into(); + dbg!(meta); + let value = MysqlValue::new(&bind.bytes, meta); + dbg!(T::from_sql(value)) + } + + #[cfg(feature = "extras")] + #[test] + fn check_all_the_types() { + let conn = crate::test_helpers::connection(); + + conn.execute("DROP TABLE IF EXISTS all_mysql_types CASCADE") + .unwrap(); + conn.execute( + "CREATE TABLE all_mysql_types ( + tiny_int TINYINT NOT NULL, + small_int SMALLINT NOT NULL, + medium_int MEDIUMINT NOT NULL, + int_col INTEGER NOT NULL, + big_int BIGINT NOT NULL, + unsigned_int INTEGER UNSIGNED NOT NULL, + zero_fill_int INTEGER ZEROFILL NOT NULL, + numeric_col NUMERIC(20,5) NOT NULL, + decimal_col DECIMAL(20,5) NOT NULL, + float_col FLOAT NOT NULL, + double_col DOUBLE NOT NULL, + bit_col BIT(8) NOT NULL, + date_col DATE NOT NULL, + date_time DATETIME NOT NULL, + timestamp_col TIMESTAMP NOT NULL, + time_col TIME NOT NULL, + year_col YEAR NOT NULL, + char_col CHAR(30) NOT NULL, + varchar_col VARCHAR(30) NOT NULL, + binary_col BINARY(30) NOT NULL, + varbinary_col VARBINARY(30) NOT NULL, + blob_col BLOB NOT NULL, + text_col TEXT NOT NULL, + enum_col ENUM('red', 'green', 'blue') NOT NULL, + set_col SET('one', 'two') NOT NULL, + geom GEOMETRY NOT NULL, + point_col POINT NOT NULL, + linestring_col LINESTRING NOT NULL, + polygon_col POLYGON NOT NULL, + multipoint_col MULTIPOINT NOT NULL, + multilinestring_col MULTILINESTRING NOT NULL, + multipolygon_col MULTIPOLYGON NOT NULL, + geometry_collection GEOMETRYCOLLECTION NOT NULL, + json_col JSON NOT NULL + )", + ) + .unwrap(); + conn + .execute( + "INSERT INTO all_mysql_types VALUES ( + 0, -- tiny_int + 1, -- small_int + 2, -- medium_int + 3, -- int_col + -5, -- big_int + 42, -- unsigned_int + 1, -- zero_fill_int + -999.999, -- numeric_col, + 3.14, -- decimal_col, + 1.23, -- float_col + 4.5678, -- double_col + b'10101010', -- bit_col + '1000-01-01', -- date_col + '9999-12-31 12:34:45.012345', -- date_time + '2020-01-01 10:10:10', -- timestamp_col + '23:01:01', -- time_col + 2020, -- year_col + 'abc', -- char_col + 'foo', -- varchar_col + 'a ', -- binary_col + 'a ', -- varbinary_col + 'binary', -- blob_col + 'some text whatever', -- text_col + 'red', -- enum_col + 'one', -- set_col + ST_GeomFromText('POINT(1 1)'), -- geom + ST_PointFromText('POINT(1 1)'), -- point_col + ST_LineStringFromText('LINESTRING(0 0,1 1,2 2)'), -- linestring_col + ST_PolygonFromText('POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7, 5 5))'), -- polygon_col + ST_MultiPointFromText('MULTIPOINT(0 0,10 10,10 20,20 20)'), -- multipoint_col + ST_MultiLineStringFromText('MULTILINESTRING((10 48,10 21,10 0),(16 0,16 23,16 48))'), -- multilinestring_col + ST_MultiPolygonFromText('MULTIPOLYGON(((28 26,28 0,84 0,84 42,28 26),(52 18,66 23,73 9,48 6,52 18)),((59 18,67 18,67 13,59 13,59 18)))'), -- multipolygon_col + ST_GeomCollFromText('GEOMETRYCOLLECTION(POINT(1 1),LINESTRING(0 0,1 1,2 2,3 3,4 4))'), -- geometry_collection + '{\"key1\": \"value1\", \"key2\": \"value2\"}' -- json_col +)", + ) + .unwrap(); + + let mut stmt = conn + .prepare_query(&crate::sql_query( + "SELECT + tiny_int, small_int, medium_int, int_col, + big_int, unsigned_int, zero_fill_int, + numeric_col, decimal_col, float_col, double_col, bit_col, + date_col, date_time, timestamp_col, time_col, year_col, + char_col, varchar_col, binary_col, varbinary_col, blob_col, + text_col, enum_col, set_col, ST_AsText(geom), ST_AsText(point_col), ST_AsText(linestring_col), + ST_AsText(polygon_col), ST_AsText(multipoint_col), ST_AsText(multilinestring_col), + ST_AsText(multipolygon_col), ST_AsText(geometry_collection), json_col + FROM all_mysql_types", + )) + .unwrap(); + + let metadata = stmt.metadata().unwrap(); + let mut output_binds = + Binds::from_output_types(vec![None; metadata.fields().len()], &metadata); + stmt.execute_statement(&mut output_binds).unwrap(); + stmt.populate_row_buffers(&mut output_binds).unwrap(); + + let results: Vec<(BindData, &_)> = output_binds + .data + .into_iter() + .zip(metadata.fields()) + .collect::>(); + + macro_rules! matches { + ($expression:expr, $( $pattern:pat )|+ $( if $guard: expr )?) => { + match $expression { + $( $pattern )|+ $( if $guard )? => true, + _ => false + } + } + } + + let tiny_int_col = &results[0].0; + assert_eq!(tiny_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_TINY); + assert!(tiny_int_col.flags.contains(Flags::NUM_FLAG)); + assert!(!tiny_int_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!(to_value::(tiny_int_col), Ok(0))); + + let small_int_col = &results[1].0; + assert_eq!(small_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_SHORT); + assert!(small_int_col.flags.contains(Flags::NUM_FLAG)); + assert!(!small_int_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!(to_value::(small_int_col), Ok(1))); + + let medium_int_col = &results[2].0; + assert_eq!(medium_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_INT24); + assert!(medium_int_col.flags.contains(Flags::NUM_FLAG)); + assert!(!medium_int_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!(to_value::(medium_int_col), Ok(2))); + + let int_col = &results[3].0; + assert_eq!(int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG); + assert!(int_col.flags.contains(Flags::NUM_FLAG)); + assert!(!int_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!(to_value::(int_col), Ok(3))); + + let big_int_col = &results[4].0; + assert_eq!(big_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONGLONG); + assert!(big_int_col.flags.contains(Flags::NUM_FLAG)); + assert!(!big_int_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!(to_value::(big_int_col), Ok(-5))); + + let unsigned_int_col = &results[5].0; + assert_eq!(unsigned_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG); + assert!(unsigned_int_col.flags.contains(Flags::NUM_FLAG)); + assert!(unsigned_int_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!( + to_value::, u32>(unsigned_int_col), + Ok(42) + )); + + let zero_fill_int_col = &results[6].0; + assert_eq!( + zero_fill_int_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_LONG + ); + assert!(zero_fill_int_col.flags.contains(Flags::NUM_FLAG)); + assert!(zero_fill_int_col.flags.contains(Flags::ZEROFILL_FLAG)); + assert!(matches!(to_value::(zero_fill_int_col), Ok(1))); + + let numeric_col = &results[7].0; + assert_eq!( + numeric_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL + ); + assert!(numeric_col.flags.contains(Flags::NUM_FLAG)); + assert!(!numeric_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert_eq!( + to_value::(numeric_col).unwrap(), + bigdecimal::BigDecimal::from_f32(-999.999).unwrap() + ); + + let decimal_col = &results[8].0; + assert_eq!( + decimal_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL + ); + assert!(decimal_col.flags.contains(Flags::NUM_FLAG)); + assert!(!decimal_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert_eq!( + to_value::(decimal_col).unwrap(), + bigdecimal::BigDecimal::from_f32(3.14).unwrap() + ); + + let float_col = &results[9].0; + assert_eq!(float_col.tpe, ffi::enum_field_types::MYSQL_TYPE_FLOAT); + assert!(float_col.flags.contains(Flags::NUM_FLAG)); + assert!(!float_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert_eq!(to_value::(float_col).unwrap(), 1.23); + + let double_col = &results[10].0; + assert_eq!(double_col.tpe, ffi::enum_field_types::MYSQL_TYPE_DOUBLE); + assert!(double_col.flags.contains(Flags::NUM_FLAG)); + assert!(!double_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert_eq!(to_value::(double_col).unwrap(), 4.5678); + + let bit_col = &results[11].0; + assert_eq!(bit_col.tpe, ffi::enum_field_types::MYSQL_TYPE_BIT); + assert!(!bit_col.flags.contains(Flags::NUM_FLAG)); + assert!(bit_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(!bit_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::>(bit_col).unwrap(), vec![170]); + + let date_col = &results[12].0; + assert_eq!(date_col.tpe, ffi::enum_field_types::MYSQL_TYPE_DATE); + assert!(!date_col.flags.contains(Flags::NUM_FLAG)); + assert_eq!( + to_value::(date_col).unwrap(), + chrono::NaiveDate::from_ymd_opt(1000, 1, 1).unwrap(), + ); + + let date_time_col = &results[13].0; + assert_eq!( + date_time_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_DATETIME + ); + assert!(!date_time_col.flags.contains(Flags::NUM_FLAG)); + assert_eq!( + to_value::(date_time_col).unwrap(), + chrono::NaiveDateTime::parse_from_str("9999-12-31 12:34:45", "%Y-%m-%d %H:%M:%S") + .unwrap() + ); + + let timestamp_col = &results[14].0; + assert_eq!( + timestamp_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_TIMESTAMP + ); + assert!(!timestamp_col.flags.contains(Flags::NUM_FLAG)); + assert_eq!( + to_value::(timestamp_col).unwrap(), + chrono::NaiveDateTime::parse_from_str("2020-01-01 10:10:10", "%Y-%m-%d %H:%M:%S") + .unwrap() + ); + + let time_col = &results[15].0; + assert_eq!(time_col.tpe, ffi::enum_field_types::MYSQL_TYPE_TIME); + assert!(!time_col.flags.contains(Flags::NUM_FLAG)); + assert_eq!( + to_value::(time_col).unwrap(), + chrono::NaiveTime::from_hms(23, 01, 01) + ); + + let year_col = &results[16].0; + assert_eq!(year_col.tpe, ffi::enum_field_types::MYSQL_TYPE_YEAR); + assert!(year_col.flags.contains(Flags::NUM_FLAG)); + assert!(year_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!(to_value::(year_col), Ok(2020))); + + let char_col = &results[17].0; + assert_eq!(char_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!char_col.flags.contains(Flags::NUM_FLAG)); + assert!(!char_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!char_col.flags.contains(Flags::SET_FLAG)); + assert!(!char_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!char_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(char_col).unwrap(), "abc"); + + let varchar_col = &results[18].0; + assert_eq!( + varchar_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_VAR_STRING + ); + assert!(!varchar_col.flags.contains(Flags::NUM_FLAG)); + assert!(!varchar_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!varchar_col.flags.contains(Flags::SET_FLAG)); + assert!(!varchar_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!varchar_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(varchar_col).unwrap(), "foo"); + + let binary_col = &results[19].0; + assert_eq!(binary_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!binary_col.flags.contains(Flags::NUM_FLAG)); + assert!(!binary_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!binary_col.flags.contains(Flags::SET_FLAG)); + assert!(!binary_col.flags.contains(Flags::ENUM_FLAG)); + assert!(binary_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::>(binary_col).unwrap(), + b"a \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + ); + + let varbinary_col = &results[20].0; + assert_eq!( + varbinary_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_VAR_STRING + ); + assert!(!varbinary_col.flags.contains(Flags::NUM_FLAG)); + assert!(!varbinary_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!varbinary_col.flags.contains(Flags::SET_FLAG)); + assert!(!varbinary_col.flags.contains(Flags::ENUM_FLAG)); + assert!(varbinary_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::>(varbinary_col).unwrap(), b"a "); + + let blob_col = &results[21].0; + assert_eq!(blob_col.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!blob_col.flags.contains(Flags::NUM_FLAG)); + assert!(blob_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!blob_col.flags.contains(Flags::SET_FLAG)); + assert!(!blob_col.flags.contains(Flags::ENUM_FLAG)); + assert!(blob_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::>(blob_col).unwrap(), b"binary"); + + let text_col = &results[22].0; + assert_eq!(text_col.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!text_col.flags.contains(Flags::NUM_FLAG)); + assert!(text_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!text_col.flags.contains(Flags::SET_FLAG)); + assert!(!text_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!text_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(text_col).unwrap(), + "some text whatever" + ); + + let enum_col = &results[23].0; + assert_eq!(enum_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!enum_col.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col.flags.contains(Flags::SET_FLAG)); + assert!(enum_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(enum_col).unwrap(), "red"); + + let set_col = &results[24].0; + assert_eq!(set_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!set_col.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col.flags.contains(Flags::SET_FLAG)); + assert!(!set_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(set_col).unwrap(), "one"); + + let geom = &results[25].0; + assert_eq!(geom.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB); + assert!(!geom.flags.contains(Flags::NUM_FLAG)); + assert!(!geom.flags.contains(Flags::BLOB_FLAG)); + assert!(!geom.flags.contains(Flags::SET_FLAG)); + assert!(!geom.flags.contains(Flags::ENUM_FLAG)); + assert!(!geom.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(geom).unwrap(), "POINT(1 1)"); + + let point_col = &results[26].0; + assert_eq!(point_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB); + assert!(!point_col.flags.contains(Flags::NUM_FLAG)); + assert!(!point_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!point_col.flags.contains(Flags::SET_FLAG)); + assert!(!point_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!point_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(point_col).unwrap(), "POINT(1 1)"); + + let linestring_col = &results[27].0; + assert_eq!( + linestring_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB + ); + assert!(!linestring_col.flags.contains(Flags::NUM_FLAG)); + assert!(!linestring_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!linestring_col.flags.contains(Flags::SET_FLAG)); + assert!(!linestring_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!linestring_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(linestring_col).unwrap(), + "LINESTRING(0 0,1 1,2 2)" + ); + + let polygon_col = &results[28].0; + assert_eq!(polygon_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB); + assert!(!polygon_col.flags.contains(Flags::NUM_FLAG)); + assert!(!polygon_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!polygon_col.flags.contains(Flags::SET_FLAG)); + assert!(!polygon_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!polygon_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(polygon_col).unwrap(), + "POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7,5 5))" + ); + + let multipoint_col = &results[29].0; + assert_eq!( + multipoint_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB + ); + assert!(!multipoint_col.flags.contains(Flags::NUM_FLAG)); + assert!(!multipoint_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!multipoint_col.flags.contains(Flags::SET_FLAG)); + assert!(!multipoint_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!multipoint_col.flags.contains(Flags::BINARY_FLAG)); + // older mysql and mariadb versions get back another encoding here + // we test for both as there seems to be no clear pattern when one or + // the other is returned + let multipoint_res = to_value::(multipoint_col).unwrap(); + assert!( + multipoint_res == "MULTIPOINT((0 0),(10 10),(10 20),(20 20))" + || multipoint_res == "MULTIPOINT(0 0,10 10,10 20,20 20)" + ); + + let multilinestring_col = &results[30].0; + assert_eq!( + multilinestring_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB + ); + assert!(!multilinestring_col.flags.contains(Flags::NUM_FLAG)); + assert!(!multilinestring_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!multilinestring_col.flags.contains(Flags::SET_FLAG)); + assert!(!multilinestring_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!multilinestring_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(multilinestring_col).unwrap(), + "MULTILINESTRING((10 48,10 21,10 0),(16 0,16 23,16 48))" + ); + + let polygon_col = &results[31].0; + assert_eq!(polygon_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB); + assert!(!polygon_col.flags.contains(Flags::NUM_FLAG)); + assert!(!polygon_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!polygon_col.flags.contains(Flags::SET_FLAG)); + assert!(!polygon_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!polygon_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(polygon_col).unwrap(), + "MULTIPOLYGON(((28 26,28 0,84 0,84 42,28 26),(52 18,66 23,73 9,48 6,52 18)),((59 18,67 18,67 13,59 13,59 18)))" + ); + + let geometry_collection = &results[32].0; + assert_eq!( + geometry_collection.tpe, + ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB + ); + assert!(!geometry_collection.flags.contains(Flags::NUM_FLAG)); + assert!(!geometry_collection.flags.contains(Flags::BLOB_FLAG)); + assert!(!geometry_collection.flags.contains(Flags::SET_FLAG)); + assert!(!geometry_collection.flags.contains(Flags::ENUM_FLAG)); + assert!(!geometry_collection.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(geometry_collection).unwrap(), + "GEOMETRYCOLLECTION(POINT(1 1),LINESTRING(0 0,1 1,2 2,3 3,4 4))" + ); + + let json_col = &results[33].0; + // mariadb >= 10.2 and mysql >=8.0 are supporting a json type + // from those mariadb >= 10.3 and mysql >= 8.0 are reporting + // json here, so we assert that we get back json + // mariadb 10.5 returns again blob + assert!( + json_col.tpe == ffi::enum_field_types::MYSQL_TYPE_JSON + || json_col.tpe == ffi::enum_field_types::MYSQL_TYPE_BLOB + ); + assert!(!json_col.flags.contains(Flags::NUM_FLAG)); + assert!(json_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col.flags.contains(Flags::SET_FLAG)); + assert!(!json_col.flags.contains(Flags::ENUM_FLAG)); + assert!(json_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(json_col).unwrap(), + "{\"key1\": \"value1\", \"key2\": \"value2\"}" + ); + } + + fn query_single_table( + query: &'static str, + conn: &MysqlConnection, + bind_tpe: impl Into<(ffi::enum_field_types, Flags)>, + ) -> BindData { + let mut stmt: Statement = conn.raw_connection.prepare(query).unwrap(); + + let bind = BindData::for_output(bind_tpe.into()); + + let mut binds = Binds { data: vec![bind] }; + + stmt.execute_statement(&mut binds).unwrap(); + stmt.populate_row_buffers(&mut binds).unwrap(); + + binds.data.remove(0) + } + + fn input_bind( + query: &'static str, + conn: &MysqlConnection, + id: i32, + (field, tpe): (Vec, impl Into<(ffi::enum_field_types, Flags)>), + ) { + let mut stmt = conn.raw_connection.prepare(query).unwrap(); + let length = field.len() as _; + let (tpe, flags) = tpe.into(); + + let field_bind = BindData { + tpe, + bytes: field, + length, + flags, + is_null: 0, + is_truncated: None, + }; + + let bytes = id.to_be_bytes().to_vec(); + let length = bytes.len() as _; + + let id_bind = BindData { + tpe: ffi::enum_field_types::MYSQL_TYPE_LONG, + bytes, + length, + flags: Flags::empty(), + is_null: 0, + is_truncated: None, + }; + + let binds = Binds { + data: vec![id_bind, field_bind], + }; + stmt.input_bind(binds).unwrap(); + stmt.did_an_error_occur().unwrap(); + unsafe { + stmt.execute().unwrap(); + } + } + + #[test] + fn check_json_bind() { + let conn: MysqlConnection = crate::test_helpers::connection(); + + table! { + json_test { + id -> Integer, + json_field -> Text, + } + } + + conn.execute("DROP TABLE IF EXISTS json_test CASCADE") + .unwrap(); + + conn.execute("CREATE TABLE json_test(id INTEGER PRIMARY KEY, json_field JSON NOT NULL)") + .unwrap(); + + conn.execute("INSERT INTO json_test(id, json_field) VALUES (1, '{\"key1\": \"value1\", \"key2\": \"value2\"}')").unwrap(); + + let json_col_as_json = query_single_table( + "SELECT json_field FROM json_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()), + ); + + assert_eq!(json_col_as_json.tpe, ffi::enum_field_types::MYSQL_TYPE_JSON); + assert!(!json_col_as_json.flags.contains(Flags::NUM_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::SET_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::ENUM_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&json_col_as_json).unwrap(), + "{\"key1\": \"value1\", \"key2\": \"value2\"}" + ); + + let json_col_as_text = query_single_table( + "SELECT json_field FROM json_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()), + ); + + assert_eq!(json_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!json_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&json_col_as_text).unwrap(), + "{\"key1\": \"value1\", \"key2\": \"value2\"}" + ); + assert_eq!(json_col_as_json.bytes, json_col_as_text.bytes); + + conn.execute("DELETE FROM json_test").unwrap(); + + input_bind( + "INSERT INTO json_test(id, json_field) VALUES (?, ?)", + &conn, + 41, + ( + b"{\"abc\": 42}".to_vec(), + MysqlType::String, + // (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()), + ), + ); + + let json_col_as_json = query_single_table( + "SELECT json_field FROM json_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()), + ); + + assert_eq!(json_col_as_json.tpe, ffi::enum_field_types::MYSQL_TYPE_JSON); + assert!(!json_col_as_json.flags.contains(Flags::NUM_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::SET_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::ENUM_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&json_col_as_json).unwrap(), + "{\"abc\": 42}" + ); + + let json_col_as_text = query_single_table( + "SELECT json_field FROM json_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()), + ); + + assert_eq!(json_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!json_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&json_col_as_text).unwrap(), + "{\"abc\": 42}" + ); + assert_eq!(json_col_as_json.bytes, json_col_as_text.bytes); + + conn.execute("DELETE FROM json_test").unwrap(); + + input_bind( + "INSERT INTO json_test(id, json_field) VALUES (?, ?)", + &conn, + 41, + (b"{\"abca\": 42}".to_vec(), MysqlType::String), + ); + + let json_col_as_json = query_single_table( + "SELECT json_field FROM json_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()), + ); + + assert_eq!(json_col_as_json.tpe, ffi::enum_field_types::MYSQL_TYPE_JSON); + assert!(!json_col_as_json.flags.contains(Flags::NUM_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::SET_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::ENUM_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&json_col_as_json).unwrap(), + "{\"abca\": 42}" + ); + + let json_col_as_text = query_single_table( + "SELECT json_field FROM json_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()), + ); + + assert_eq!(json_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!json_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&json_col_as_text).unwrap(), + "{\"abca\": 42}" + ); + assert_eq!(json_col_as_json.bytes, json_col_as_text.bytes); + } + + #[test] + fn check_enum_bind() { + let conn: MysqlConnection = crate::test_helpers::connection(); + + conn.execute("DROP TABLE IF EXISTS enum_test CASCADE") + .unwrap(); + + conn.execute("CREATE TABLE enum_test(id INTEGER PRIMARY KEY, enum_field ENUM('red', 'green', 'blue') NOT NULL)") + .unwrap(); + + conn.execute("INSERT INTO enum_test(id, enum_field) VALUES (1, 'green')") + .unwrap(); + + let enum_col_as_enum: BindData = + query_single_table("SELECT enum_field FROM enum_test", &conn, MysqlType::Enum); + + assert_eq!( + enum_col_as_enum.tpe, + ffi::enum_field_types::MYSQL_TYPE_STRING + ); + assert!(!enum_col_as_enum.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_enum.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&enum_col_as_enum).unwrap(), + "green" + ); + + for tpe in &[ + ffi::enum_field_types::MYSQL_TYPE_BLOB, + ffi::enum_field_types::MYSQL_TYPE_VAR_STRING, + ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB, + ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB, + ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB, + ] { + let enum_col_as_text = query_single_table( + "SELECT enum_field FROM enum_test", + &conn, + (*tpe, Flags::ENUM_FLAG), + ); + + assert_eq!(enum_col_as_text.tpe, *tpe); + assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&enum_col_as_text).unwrap(), + "green" + ); + assert_eq!(enum_col_as_enum.bytes, enum_col_as_text.bytes); + } + + let enum_col_as_text = query_single_table( + "SELECT enum_field FROM enum_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()), + ); + + assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&enum_col_as_text).unwrap(), + "green" + ); + assert_eq!(enum_col_as_enum.bytes, enum_col_as_text.bytes); + + conn.execute("DELETE FROM enum_test").unwrap(); + + input_bind( + "INSERT INTO enum_test(id, enum_field) VALUES (?, ?)", + &conn, + 41, + (b"blue".to_vec(), MysqlType::Enum), + ); + + let enum_col_as_enum = + query_single_table("SELECT enum_field FROM enum_test", &conn, MysqlType::Enum); + + assert_eq!( + enum_col_as_enum.tpe, + ffi::enum_field_types::MYSQL_TYPE_STRING + ); + assert!(!enum_col_as_enum.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_enum.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&enum_col_as_enum).unwrap(), "blue"); + + let enum_col_as_text = query_single_table( + "SELECT enum_field FROM enum_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG), + ); + + assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&enum_col_as_text).unwrap(), "blue"); + assert_eq!(enum_col_as_enum.bytes, enum_col_as_text.bytes); + + let enum_col_as_text = query_single_table( + "SELECT enum_field FROM enum_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG), + ); + + assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&enum_col_as_text).unwrap(), "blue"); + assert_eq!(enum_col_as_enum.bytes, enum_col_as_text.bytes); + + conn.execute("DELETE FROM enum_test").unwrap(); + + input_bind( + "INSERT INTO enum_test(id, enum_field) VALUES (?, ?)", + &conn, + 41, + ( + b"red".to_vec(), + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG), + ), + ); + + let enum_col_as_enum = + query_single_table("SELECT enum_field FROM enum_test", &conn, MysqlType::Enum); + + assert_eq!( + enum_col_as_enum.tpe, + ffi::enum_field_types::MYSQL_TYPE_STRING + ); + assert!(!enum_col_as_enum.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_enum.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&enum_col_as_enum).unwrap(), "red"); + + let enum_col_as_text = query_single_table( + "SELECT enum_field FROM enum_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG), + ); + + assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&enum_col_as_text).unwrap(), "red"); + assert_eq!(enum_col_as_enum.bytes, enum_col_as_text.bytes); + } + + #[test] + fn check_set_bind() { + let conn: MysqlConnection = crate::test_helpers::connection(); + + conn.execute("DROP TABLE IF EXISTS set_test CASCADE") + .unwrap(); + + conn.execute("CREATE TABLE set_test(id INTEGER PRIMARY KEY, set_field SET('red', 'green', 'blue') NOT NULL)") + .unwrap(); + + conn.execute("INSERT INTO set_test(id, set_field) VALUES (1, 'green')") + .unwrap(); + + let set_col_as_set: BindData = + query_single_table("SELECT set_field FROM set_test", &conn, MysqlType::Set); + + assert_eq!(set_col_as_set.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!set_col_as_set.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col_as_set.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_set).unwrap(), "green"); + + for tpe in &[ + ffi::enum_field_types::MYSQL_TYPE_BLOB, + ffi::enum_field_types::MYSQL_TYPE_VAR_STRING, + ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB, + ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB, + ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB, + ] { + let set_col_as_text = query_single_table( + "SELECT set_field FROM set_test", + &conn, + (*tpe, Flags::SET_FLAG), + ); + + assert_eq!(set_col_as_text.tpe, *tpe); + assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_text).unwrap(), "green"); + assert_eq!(set_col_as_set.bytes, set_col_as_text.bytes); + } + let set_col_as_text = query_single_table( + "SELECT set_field FROM set_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()), + ); + + assert_eq!(set_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_text).unwrap(), "green"); + assert_eq!(set_col_as_set.bytes, set_col_as_text.bytes); + + conn.execute("DELETE FROM set_test").unwrap(); + + input_bind( + "INSERT INTO set_test(id, set_field) VALUES (?, ?)", + &conn, + 41, + (b"blue".to_vec(), MysqlType::Set), + ); + + let set_col_as_set = + query_single_table("SELECT set_field FROM set_test", &conn, MysqlType::Set); + + assert_eq!(set_col_as_set.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!set_col_as_set.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col_as_set.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_set).unwrap(), "blue"); + + let set_col_as_text = query_single_table( + "SELECT set_field FROM set_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::SET_FLAG), + ); + + assert_eq!(set_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_text).unwrap(), "blue"); + assert_eq!(set_col_as_set.bytes, set_col_as_text.bytes); + + conn.execute("DELETE FROM set_test").unwrap(); + + input_bind( + "INSERT INTO set_test(id, set_field) VALUES (?, ?)", + &conn, + 41, + (b"red".to_vec(), MysqlType::String), + ); + + let set_col_as_set = + query_single_table("SELECT set_field FROM set_test", &conn, MysqlType::Set); + + assert_eq!(set_col_as_set.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!set_col_as_set.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col_as_set.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_set).unwrap(), "red"); + + let set_col_as_text = query_single_table( + "SELECT set_field FROM set_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::SET_FLAG), + ); + + assert_eq!(set_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_text).unwrap(), "red"); + assert_eq!(set_col_as_set.bytes, set_col_as_text.bytes); + } +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/connection/mod.rs b/collector/benchmarks/diesel/diesel/src/mysql/connection/mod.rs new file mode 100644 index 000000000..e9aa3689d --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/connection/mod.rs @@ -0,0 +1,162 @@ +mod bind; +mod raw; +mod stmt; +mod url; + +use self::raw::RawConnection; +use self::stmt::Statement; +use self::url::ConnectionOptions; +use super::backend::Mysql; +use crate::connection::*; +use crate::deserialize::FromSqlRow; +use crate::expression::QueryMetadata; +use crate::query_builder::bind_collector::RawBytesBindCollector; +use crate::query_builder::*; +use crate::result::*; + +#[allow(missing_debug_implementations, missing_copy_implementations)] +/// A connection to a MySQL database. Connection URLs should be in the form +/// `mysql://[user[:password]@]host/database_name` +pub struct MysqlConnection { + raw_connection: RawConnection, + transaction_manager: AnsiTransactionManager, + statement_cache: StatementCache, +} + +unsafe impl Send for MysqlConnection {} + +impl SimpleConnection for MysqlConnection { + fn batch_execute(&self, query: &str) -> QueryResult<()> { + self.raw_connection + .enable_multi_statements(|| self.raw_connection.execute(query)) + } +} + +impl Connection for MysqlConnection { + type Backend = Mysql; + type TransactionManager = AnsiTransactionManager; + + fn establish(database_url: &str) -> ConnectionResult { + use crate::result::ConnectionError::CouldntSetupConfiguration; + + let raw_connection = RawConnection::new(); + let connection_options = ConnectionOptions::parse(database_url)?; + raw_connection.connect(&connection_options)?; + let conn = MysqlConnection { + raw_connection: raw_connection, + transaction_manager: AnsiTransactionManager::new(), + statement_cache: StatementCache::new(), + }; + conn.set_config_options() + .map_err(CouldntSetupConfiguration)?; + Ok(conn) + } + + #[doc(hidden)] + fn execute(&self, query: &str) -> QueryResult { + self.raw_connection + .execute(query) + .map(|_| self.raw_connection.affected_rows()) + } + + #[doc(hidden)] + fn load(&self, source: T) -> QueryResult> + where + T: AsQuery, + T::Query: QueryFragment + QueryId, + U: FromSqlRow, + Self::Backend: QueryMetadata, + { + use crate::result::Error::DeserializationError; + + let mut stmt = self.prepare_query(&source.as_query())?; + let mut metadata = Vec::new(); + Mysql::row_metadata(&(), &mut metadata); + let results = unsafe { stmt.results(metadata)? }; + results.map(|row| U::build_from_row(&row).map_err(DeserializationError)) + } + + #[doc(hidden)] + fn execute_returning_count(&self, source: &T) -> QueryResult + where + T: QueryFragment + QueryId, + { + let stmt = self.prepare_query(source)?; + unsafe { + stmt.execute()?; + } + Ok(stmt.affected_rows()) + } + + #[doc(hidden)] + fn transaction_manager(&self) -> &Self::TransactionManager { + &self.transaction_manager + } +} + +impl MysqlConnection { + fn prepare_query(&self, source: &T) -> QueryResult> + where + T: QueryFragment + QueryId, + { + let mut stmt = self + .statement_cache + .cached_statement(source, &[], |sql| self.raw_connection.prepare(sql))?; + let mut bind_collector = RawBytesBindCollector::new(); + source.collect_binds(&mut bind_collector, &())?; + let binds = bind_collector + .metadata + .into_iter() + .zip(bind_collector.binds); + stmt.bind(binds)?; + Ok(stmt) + } + + fn set_config_options(&self) -> QueryResult<()> { + self.execute("SET sql_mode=(SELECT CONCAT(@@sql_mode, ',PIPES_AS_CONCAT'))")?; + self.execute("SET time_zone = '+00:00';")?; + self.execute("SET character_set_client = 'utf8mb4'")?; + self.execute("SET character_set_connection = 'utf8mb4'")?; + self.execute("SET character_set_results = 'utf8mb4'")?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + extern crate dotenv; + + use super::*; + use std::env; + + fn connection() -> MysqlConnection { + let _ = dotenv::dotenv(); + let database_url = env::var("MYSQL_UNIT_TEST_DATABASE_URL") + .or_else(|_| env::var("MYSQL_DATABASE_URL")) + .or_else(|_| env::var("DATABASE_URL")) + .expect("DATABASE_URL must be set in order to run unit tests"); + MysqlConnection::establish(&database_url).unwrap() + } + + #[test] + fn batch_execute_handles_single_queries_with_results() { + let connection = connection(); + assert!(connection.batch_execute("SELECT 1").is_ok()); + assert!(connection.batch_execute("SELECT 1").is_ok()); + } + + #[test] + fn batch_execute_handles_multi_queries_with_results() { + let connection = connection(); + let query = "SELECT 1; SELECT 2; SELECT 3;"; + assert!(connection.batch_execute(query).is_ok()); + assert!(connection.batch_execute(query).is_ok()); + } + + #[test] + fn execute_handles_queries_which_return_results() { + let connection = connection(); + assert!(connection.execute("SELECT 1").is_ok()); + assert!(connection.execute("SELECT 1").is_ok()); + } +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/connection/raw.rs b/collector/benchmarks/diesel/diesel/src/mysql/connection/raw.rs new file mode 100644 index 000000000..be771cef2 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/connection/raw.rs @@ -0,0 +1,218 @@ +extern crate mysqlclient_sys as ffi; + +use std::ffi::CStr; +use std::os::raw as libc; +use std::ptr::{self, NonNull}; +use std::sync::Once; + +use super::stmt::Statement; +use super::url::ConnectionOptions; +use crate::result::{ConnectionError, ConnectionResult, QueryResult}; + +pub struct RawConnection(NonNull); + +impl RawConnection { + pub fn new() -> Self { + perform_thread_unsafe_library_initialization(); + let raw_connection = unsafe { ffi::mysql_init(ptr::null_mut()) }; + // We're trusting https://dev.mysql.com/doc/refman/5.7/en/mysql-init.html + // that null return always means OOM + let raw_connection = + NonNull::new(raw_connection).expect("Insufficient memory to allocate connection"); + let result = RawConnection(raw_connection); + + // This is only non-zero for unrecognized options, which should never happen. + let charset_result = unsafe { + ffi::mysql_options( + result.0.as_ptr(), + ffi::mysql_option::MYSQL_SET_CHARSET_NAME, + b"utf8mb4\0".as_ptr() as *const libc::c_void, + ) + }; + assert_eq!( + 0, charset_result, + "MYSQL_SET_CHARSET_NAME was not \ + recognized as an option by MySQL. This should never \ + happen." + ); + + result + } + + pub fn connect(&self, connection_options: &ConnectionOptions) -> ConnectionResult<()> { + let host = connection_options.host(); + let user = connection_options.user(); + let password = connection_options.password(); + let database = connection_options.database(); + let port = connection_options.port(); + let unix_socket = connection_options.unix_socket(); + + unsafe { + // Make sure you don't use the fake one! + ffi::mysql_real_connect( + self.0.as_ptr(), + host.map(CStr::as_ptr).unwrap_or_else(|| ptr::null_mut()), + user.as_ptr(), + password + .map(CStr::as_ptr) + .unwrap_or_else(|| ptr::null_mut()), + database + .map(CStr::as_ptr) + .unwrap_or_else(|| ptr::null_mut()), + u32::from(port.unwrap_or(0)), + unix_socket + .map(CStr::as_ptr) + .unwrap_or_else(|| ptr::null_mut()), + 0, + ) + }; + + let last_error_message = self.last_error_message(); + if last_error_message.is_empty() { + Ok(()) + } else { + Err(ConnectionError::BadConnection(last_error_message)) + } + } + + pub fn last_error_message(&self) -> String { + unsafe { CStr::from_ptr(ffi::mysql_error(self.0.as_ptr())) } + .to_string_lossy() + .into_owned() + } + + pub fn execute(&self, query: &str) -> QueryResult<()> { + unsafe { + // Make sure you don't use the fake one! + ffi::mysql_real_query( + self.0.as_ptr(), + query.as_ptr() as *const libc::c_char, + query.len() as libc::c_ulong, + ); + } + self.did_an_error_occur()?; + self.flush_pending_results()?; + Ok(()) + } + + pub fn enable_multi_statements(&self, f: F) -> QueryResult + where + F: FnOnce() -> QueryResult, + { + unsafe { + ffi::mysql_set_server_option( + self.0.as_ptr(), + ffi::enum_mysql_set_option::MYSQL_OPTION_MULTI_STATEMENTS_ON, + ); + } + self.did_an_error_occur()?; + + let result = f(); + + unsafe { + ffi::mysql_set_server_option( + self.0.as_ptr(), + ffi::enum_mysql_set_option::MYSQL_OPTION_MULTI_STATEMENTS_OFF, + ); + } + self.did_an_error_occur()?; + + result + } + + pub fn affected_rows(&self) -> usize { + let affected_rows = unsafe { ffi::mysql_affected_rows(self.0.as_ptr()) }; + affected_rows as usize + } + + pub fn prepare(&self, query: &str) -> QueryResult { + let stmt = unsafe { ffi::mysql_stmt_init(self.0.as_ptr()) }; + // It is documented that the only reason `mysql_stmt_init` will fail + // is because of OOM. + // https://dev.mysql.com/doc/refman/5.7/en/mysql-stmt-init.html + let stmt = NonNull::new(stmt).expect("Out of memory creating prepared statement"); + let stmt = Statement::new(stmt); + stmt.prepare(query)?; + Ok(stmt) + } + + fn did_an_error_occur(&self) -> QueryResult<()> { + use crate::result::DatabaseErrorKind; + use crate::result::Error::DatabaseError; + + let error_message = self.last_error_message(); + if error_message.is_empty() { + Ok(()) + } else { + Err(DatabaseError( + DatabaseErrorKind::__Unknown, + Box::new(error_message), + )) + } + } + + fn flush_pending_results(&self) -> QueryResult<()> { + // We may have a result to process before advancing + self.consume_current_result()?; + while self.more_results() { + self.next_result()?; + self.consume_current_result()?; + } + Ok(()) + } + + fn consume_current_result(&self) -> QueryResult<()> { + unsafe { + let res = ffi::mysql_store_result(self.0.as_ptr()); + if !res.is_null() { + ffi::mysql_free_result(res); + } + } + self.did_an_error_occur() + } + + fn more_results(&self) -> bool { + unsafe { ffi::mysql_more_results(self.0.as_ptr()) != 0 } + } + + fn next_result(&self) -> QueryResult<()> { + unsafe { ffi::mysql_next_result(self.0.as_ptr()) }; + self.did_an_error_occur() + } +} + +impl Drop for RawConnection { + fn drop(&mut self) { + unsafe { + ffi::mysql_close(self.0.as_ptr()); + } + } +} + +/// > In a non-multi-threaded environment, `mysql_init()` invokes +/// > `mysql_library_init()` automatically as necessary. However, +/// > `mysql_library_init()` is not thread-safe in a multi-threaded environment, +/// > and thus neither is `mysql_init()`. Before calling `mysql_init()`, either +/// > call `mysql_library_init()` prior to spawning any threads, or use a mutex +/// > to protect the `mysql_library_init()` call. This should be done prior to +/// > any other client library call. +/// +/// +static MYSQL_THREAD_UNSAFE_INIT: Once = Once::new(); + +fn perform_thread_unsafe_library_initialization() { + MYSQL_THREAD_UNSAFE_INIT.call_once(|| { + // mysql_library_init is defined by `#define mysql_library_init mysql_server_init` + // which isn't picked up by bindgen + let error_code = unsafe { ffi::mysql_server_init(0, ptr::null_mut(), ptr::null_mut()) }; + if error_code != 0 { + // FIXME: This is documented as Nonzero if an error occurred. + // Presumably the value has some sort of meaning that we should + // reflect in this message. We are going to panic instead of return + // an error here, since the documentation does not indicate whether + // it is safe to call this function twice if the first call failed, + // so I will assume it is not. + panic!("Unable to perform MySQL global initialization"); + } + }) +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/iterator.rs b/collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/iterator.rs new file mode 100644 index 000000000..584b66f94 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/iterator.rs @@ -0,0 +1,122 @@ +use super::{metadata::MysqlFieldMetadata, BindData, Binds, Statement, StatementMetadata}; +use crate::mysql::{Mysql, MysqlType}; +use crate::result::QueryResult; +use crate::row::*; + +pub struct StatementIterator<'a> { + stmt: &'a mut Statement, + output_binds: Binds, + metadata: StatementMetadata, +} + +#[allow(clippy::should_implement_trait)] // don't neet `Iterator` here +impl<'a> StatementIterator<'a> { + #[allow(clippy::new_ret_no_self)] + pub fn new(stmt: &'a mut Statement, types: Vec>) -> QueryResult { + let metadata = stmt.metadata()?; + + let mut output_binds = Binds::from_output_types(types, &metadata); + + stmt.execute_statement(&mut output_binds)?; + + Ok(StatementIterator { + stmt, + output_binds, + metadata, + }) + } + + pub fn map(mut self, mut f: F) -> QueryResult> + where + F: FnMut(MysqlRow) -> QueryResult, + { + let mut results = Vec::new(); + while let Some(row) = self.next() { + results.push(f(row?)?); + } + Ok(results) + } + + fn next(&mut self) -> Option> { + match self.stmt.populate_row_buffers(&mut self.output_binds) { + Ok(Some(())) => Some(Ok(MysqlRow { + col_idx: 0, + binds: &mut self.output_binds, + metadata: &self.metadata, + })), + Ok(None) => None, + Err(e) => Some(Err(e)), + } + } +} + +#[derive(Clone)] +pub struct MysqlRow<'a> { + col_idx: usize, + binds: &'a Binds, + metadata: &'a StatementMetadata, +} + +impl<'a> Row<'a, Mysql> for MysqlRow<'a> { + type Field = MysqlField<'a>; + type InnerPartialRow = Self; + + fn field_count(&self) -> usize { + self.binds.len() + } + + fn get(&self, idx: I) -> Option + where + Self: RowIndex, + { + let idx = self.idx(idx)?; + Some(MysqlField { + bind: &self.binds[idx], + metadata: &self.metadata.fields()[idx], + }) + } + + fn partial_row(&self, range: std::ops::Range) -> PartialRow { + PartialRow::new(self, range) + } +} + +impl<'a> RowIndex for MysqlRow<'a> { + fn idx(&self, idx: usize) -> Option { + if idx < self.field_count() { + Some(idx) + } else { + None + } + } +} + +impl<'a, 'b> RowIndex<&'a str> for MysqlRow<'b> { + fn idx(&self, idx: &'a str) -> Option { + self.metadata + .fields() + .iter() + .enumerate() + .find(|(_, field_meta)| field_meta.field_name() == Some(idx)) + .map(|(idx, _)| idx) + } +} + +pub struct MysqlField<'a> { + bind: &'a BindData, + metadata: &'a MysqlFieldMetadata<'a>, +} + +impl<'a> Field<'a, Mysql> for MysqlField<'a> { + fn field_name(&self) -> Option<&'a str> { + self.metadata.field_name() + } + + fn is_null(&self) -> bool { + self.bind.is_null() + } + + fn value(&self) -> Option> { + self.bind.value() + } +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/metadata.rs b/collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/metadata.rs new file mode 100644 index 000000000..7a79ee92c --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/metadata.rs @@ -0,0 +1,60 @@ +use std::ffi::CStr; +use std::ptr::NonNull; +use std::slice; + +use super::ffi; +use crate::mysql::connection::bind::Flags; + +pub struct StatementMetadata { + result: NonNull, +} + +impl StatementMetadata { + pub fn new(result: NonNull) -> Self { + StatementMetadata { result } + } + + pub fn fields(&'_ self) -> &'_ [MysqlFieldMetadata<'_>] { + unsafe { + let num_fields = ffi::mysql_num_fields(self.result.as_ptr()); + let field_ptr = ffi::mysql_fetch_fields(self.result.as_ptr()); + if field_ptr.is_null() { + &[] + } else { + slice::from_raw_parts(field_ptr as _, num_fields as usize) + } + } + } +} + +impl Drop for StatementMetadata { + fn drop(&mut self) { + unsafe { ffi::mysql_free_result(self.result.as_mut()) }; + } +} + +#[repr(transparent)] +pub struct MysqlFieldMetadata<'a>(ffi::MYSQL_FIELD, std::marker::PhantomData<&'a ()>); + +impl<'a> MysqlFieldMetadata<'a> { + pub fn field_name(&self) -> Option<&str> { + if self.0.name.is_null() { + None + } else { + unsafe { + Some(CStr::from_ptr(self.0.name).to_str().expect( + "Expect mysql field names to be UTF-8, because we \ + requested UTF-8 encoding on connection setup", + )) + } + } + } + + pub fn field_type(&self) -> ffi::enum_field_types { + self.0.type_ + } + + pub(crate) fn flags(&self) -> Flags { + Flags::from(self.0.flags) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/mod.rs b/collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/mod.rs new file mode 100644 index 000000000..7fb043327 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/mod.rs @@ -0,0 +1,180 @@ +extern crate mysqlclient_sys as ffi; + +mod iterator; +mod metadata; + +use std::ffi::CStr; +use std::os::raw as libc; +use std::ptr::NonNull; + +use self::iterator::*; +use super::bind::{BindData, Binds}; +use crate::mysql::MysqlType; +use crate::result::{DatabaseErrorKind, QueryResult}; + +pub use self::metadata::StatementMetadata; + +pub struct Statement { + stmt: NonNull, + input_binds: Option, +} + +impl Statement { + pub(crate) fn new(stmt: NonNull) -> Self { + Statement { + stmt, + input_binds: None, + } + } + + pub fn prepare(&self, query: &str) -> QueryResult<()> { + unsafe { + ffi::mysql_stmt_prepare( + self.stmt.as_ptr(), + query.as_ptr() as *const libc::c_char, + query.len() as libc::c_ulong, + ); + } + self.did_an_error_occur() + } + + pub fn bind(&mut self, binds: Iter) -> QueryResult<()> + where + Iter: IntoIterator>)>, + { + let input_binds = Binds::from_input_data(binds)?; + self.input_bind(input_binds) + } + + pub(super) fn input_bind(&mut self, mut input_binds: Binds) -> QueryResult<()> { + input_binds.with_mysql_binds(|bind_ptr| { + // This relies on the invariant that the current value of `self.input_binds` + // will not change without this function being called + unsafe { + ffi::mysql_stmt_bind_param(self.stmt.as_ptr(), bind_ptr); + } + }); + self.input_binds = Some(input_binds); + self.did_an_error_occur() + } + + /// This function should be called instead of `results` on queries which + /// have no return value. It should never be called on a statement on + /// which `results` has previously been called? + pub unsafe fn execute(&self) -> QueryResult<()> { + ffi::mysql_stmt_execute(self.stmt.as_ptr()); + self.did_an_error_occur()?; + ffi::mysql_stmt_store_result(self.stmt.as_ptr()); + self.did_an_error_occur()?; + Ok(()) + } + + pub fn affected_rows(&self) -> usize { + let affected_rows = unsafe { ffi::mysql_stmt_affected_rows(self.stmt.as_ptr()) }; + affected_rows as usize + } + + /// This function should be called instead of `execute` for queries which + /// have a return value. After calling this function, `execute` can never + /// be called on this statement. + pub unsafe fn results( + &mut self, + types: Vec>, + ) -> QueryResult { + StatementIterator::new(self, types) + } + + fn last_error_message(&self) -> String { + unsafe { CStr::from_ptr(ffi::mysql_stmt_error(self.stmt.as_ptr())) } + .to_string_lossy() + .into_owned() + } + + /// If the pointers referenced by the `MYSQL_BIND` structures are invalidated, + /// you must call this function again before calling `mysql_stmt_fetch`. + pub unsafe fn bind_result(&self, binds: *mut ffi::MYSQL_BIND) -> QueryResult<()> { + ffi::mysql_stmt_bind_result(self.stmt.as_ptr(), binds); + self.did_an_error_occur() + } + + pub unsafe fn fetch_column( + &self, + bind: &mut ffi::MYSQL_BIND, + idx: usize, + offset: usize, + ) -> QueryResult<()> { + ffi::mysql_stmt_fetch_column( + self.stmt.as_ptr(), + bind, + idx as libc::c_uint, + offset as libc::c_ulong, + ); + self.did_an_error_occur() + } + + pub(super) fn metadata(&self) -> QueryResult { + use crate::result::Error::DeserializationError; + + let result_ptr = unsafe { ffi::mysql_stmt_result_metadata(self.stmt.as_ptr()) }; + self.did_an_error_occur()?; + NonNull::new(result_ptr) + .map(StatementMetadata::new) + .ok_or_else(|| DeserializationError("No metadata exists".into())) + } + + pub(super) fn did_an_error_occur(&self) -> QueryResult<()> { + use crate::result::Error::DatabaseError; + + let error_message = self.last_error_message(); + if error_message.is_empty() { + Ok(()) + } else { + Err(DatabaseError( + self.last_error_type(), + Box::new(error_message), + )) + } + } + + fn last_error_type(&self) -> DatabaseErrorKind { + let last_error_number = unsafe { ffi::mysql_stmt_errno(self.stmt.as_ptr()) }; + // These values are not exposed by the C API, but are documented + // at https://dev.mysql.com/doc/refman/8.0/en/server-error-reference.html + // and are from the ANSI SQLSTATE standard + match last_error_number { + 1062 | 1586 | 1859 => DatabaseErrorKind::UniqueViolation, + 1216 | 1217 | 1451 | 1452 | 1830 | 1834 => DatabaseErrorKind::ForeignKeyViolation, + 1792 => DatabaseErrorKind::ReadOnlyTransaction, + 1048 | 1364 => DatabaseErrorKind::NotNullViolation, + 3819 => DatabaseErrorKind::CheckViolation, + _ => DatabaseErrorKind::__Unknown, + } + } + + pub(super) fn execute_statement(&mut self, binds: &mut Binds) -> QueryResult<()> { + unsafe { + binds.with_mysql_binds(|bind_ptr| self.bind_result(bind_ptr))?; + self.execute()?; + } + Ok(()) + } + + pub(super) fn populate_row_buffers(&self, binds: &mut Binds) -> QueryResult> { + let next_row_result = unsafe { ffi::mysql_stmt_fetch(self.stmt.as_ptr()) }; + match next_row_result as libc::c_uint { + ffi::MYSQL_NO_DATA => Ok(None), + ffi::MYSQL_DATA_TRUNCATED => binds.populate_dynamic_buffers(self).map(Some), + 0 => { + binds.update_buffer_lengths(); + Ok(Some(())) + } + _error => self.did_an_error_occur().map(Some), + } + } +} + +impl Drop for Statement { + fn drop(&mut self) { + unsafe { ffi::mysql_stmt_close(self.stmt.as_ptr()) }; + } +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/connection/url.rs b/collector/benchmarks/diesel/diesel/src/mysql/connection/url.rs new file mode 100644 index 000000000..5fc84622f --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/connection/url.rs @@ -0,0 +1,229 @@ +extern crate percent_encoding; +extern crate url; + +use self::percent_encoding::percent_decode; +use self::url::{Host, Url}; +use std::collections::HashMap; +use std::ffi::{CStr, CString}; + +use crate::result::{ConnectionError, ConnectionResult}; + +pub struct ConnectionOptions { + host: Option, + user: CString, + password: Option, + database: Option, + port: Option, + unix_socket: Option, +} + +impl ConnectionOptions { + pub fn parse(database_url: &str) -> ConnectionResult { + let url = match Url::parse(database_url) { + Ok(url) => url, + Err(_) => return Err(connection_url_error()), + }; + + if url.scheme() != "mysql" { + return Err(connection_url_error()); + } + + if url.path_segments().map(Iterator::count).unwrap_or(0) > 1 { + return Err(connection_url_error()); + } + + let query_pairs = url.query_pairs().into_owned().collect::>(); + if query_pairs.get("database").is_some() { + return Err(connection_url_error()); + } + + let unix_socket = match query_pairs.get("unix_socket") { + Some(v) => Some(CString::new(v.as_bytes())?), + _ => None, + }; + + let host = match url.host() { + Some(Host::Ipv6(host)) => Some(CString::new(host.to_string())?), + Some(host) if host.to_string() == "localhost" && unix_socket != None => None, + Some(host) => Some(CString::new(host.to_string())?), + None => None, + }; + let user = decode_into_cstring(url.username())?; + let password = match url.password() { + Some(password) => Some(decode_into_cstring(password)?), + None => None, + }; + + let database = match url.path_segments().and_then(|mut iter| iter.next()) { + Some("") | None => None, + Some(segment) => Some(CString::new(segment.as_bytes())?), + }; + + Ok(ConnectionOptions { + host: host, + user: user, + password: password, + database: database, + port: url.port(), + unix_socket: unix_socket, + }) + } + + pub fn host(&self) -> Option<&CStr> { + self.host.as_ref().map(|x| &**x) + } + + pub fn user(&self) -> &CStr { + &self.user + } + + pub fn password(&self) -> Option<&CStr> { + self.password.as_ref().map(|x| &**x) + } + + pub fn database(&self) -> Option<&CStr> { + self.database.as_ref().map(|x| &**x) + } + + pub fn port(&self) -> Option { + self.port + } + + pub fn unix_socket(&self) -> Option<&CStr> { + self.unix_socket.as_ref().map(|x| &**x) + } +} + +fn decode_into_cstring(s: &str) -> ConnectionResult { + let decoded = percent_decode(s.as_bytes()) + .decode_utf8() + .map_err(|_| connection_url_error())?; + CString::new(decoded.as_bytes()).map_err(Into::into) +} + +fn connection_url_error() -> ConnectionError { + let msg = "MySQL connection URLs must be in the form \ + `mysql://[[user]:[password]@]host[:port][/database][?unix_socket=socket-path]`"; + ConnectionError::InvalidConnectionUrl(msg.into()) +} + +#[test] +fn urls_with_schemes_other_than_mysql_are_errors() { + assert!(ConnectionOptions::parse("postgres://localhost").is_err()); + assert!(ConnectionOptions::parse("http://localhost").is_err()); + assert!(ConnectionOptions::parse("file:///tmp/mysql.sock").is_err()); + assert!(ConnectionOptions::parse("socket:///tmp/mysql.sock").is_err()); + assert!(ConnectionOptions::parse("mysql://localhost?database=somedb").is_err()); + assert!(ConnectionOptions::parse("mysql://localhost").is_ok()); +} + +#[test] +fn urls_must_have_zero_or_one_path_segments() { + assert!(ConnectionOptions::parse("mysql://localhost/foo/bar").is_err()); + assert!(ConnectionOptions::parse("mysql://localhost/foo").is_ok()); +} + +#[test] +fn first_path_segment_is_treated_as_database() { + let foo_cstr = CString::new("foo").unwrap(); + let bar_cstr = CString::new("bar").unwrap(); + assert_eq!( + Some(&*foo_cstr), + ConnectionOptions::parse("mysql://localhost/foo") + .unwrap() + .database() + ); + assert_eq!( + Some(&*bar_cstr), + ConnectionOptions::parse("mysql://localhost/bar") + .unwrap() + .database() + ); + assert_eq!( + None, + ConnectionOptions::parse("mysql://localhost") + .unwrap() + .database() + ); +} + +#[test] +fn userinfo_should_be_percent_decode() { + use self::percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS}; + const USERINFO_ENCODE_SET: &AsciiSet = &CONTROLS + .add(b' ') + .add(b'"') + .add(b'<') + .add(b'>') + .add(b'`') + .add(b'#') + .add(b'?') + .add(b'{') + .add(b'}') + .add(b'/') + .add(b':') + .add(b';') + .add(b'=') + .add(b'@') + .add(b'[') + .add(b'\\') + .add(b']') + .add(b'^') + .add(b'|'); + + let username = "x#gfuL?4Zuj{n73m}eeJt0"; + let encoded_username = utf8_percent_encode(username, USERINFO_ENCODE_SET); + + let password = "x/gfuL?4Zuj{n73m}eeJt1"; + let encoded_password = utf8_percent_encode(password, USERINFO_ENCODE_SET); + + let db_url = format!( + "mysql://{}:{}@localhost/bar", + encoded_username, encoded_password + ); + let db_url = Url::parse(&db_url).unwrap(); + + let conn_opts = ConnectionOptions::parse(db_url.as_str()).unwrap(); + let username = CString::new(username.as_bytes()).unwrap(); + let password = CString::new(password.as_bytes()).unwrap(); + assert_eq!(username, conn_opts.user); + assert_eq!(password, conn_opts.password.unwrap()); +} + +#[test] +fn ipv6_host_not_wrapped_in_brackets() { + let host1 = CString::new("::1").unwrap(); + let host2 = CString::new("2001:db8:85a3::8a2e:370:7334").unwrap(); + + assert_eq!( + Some(&*host1), + ConnectionOptions::parse("mysql://[::1]").unwrap().host() + ); + assert_eq!( + Some(&*host2), + ConnectionOptions::parse("mysql://[2001:db8:85a3::8a2e:370:7334]") + .unwrap() + .host() + ); +} + +#[test] +fn unix_socket_tests() { + let unix_socket = "/var/run/mysqld.sock"; + let username = "foo"; + let password = "bar"; + let db_url = format!( + "mysql://{}:{}@localhost?unix_socket={}", + username, password, unix_socket + ); + let conn_opts = ConnectionOptions::parse(db_url.as_str()).unwrap(); + let cstring = |s| CString::new(s).unwrap(); + assert_eq!(None, conn_opts.host); + assert_eq!(None, conn_opts.port); + assert_eq!(cstring(username), conn_opts.user); + assert_eq!(cstring(password), conn_opts.password.unwrap()); + assert_eq!( + CString::new(unix_socket).unwrap(), + conn_opts.unix_socket.unwrap() + ); +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/mod.rs b/collector/benchmarks/diesel/diesel/src/mysql/mod.rs new file mode 100644 index 000000000..8f3161254 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/mod.rs @@ -0,0 +1,17 @@ +//! Provides types and functions related to working with MySQL +//! +//! Much of this module is re-exported from database agnostic locations. +//! However, if you are writing code specifically to extend Diesel on +//! MySQL, you may need to work with this module directly. + +mod backend; +mod connection; +mod value; + +mod query_builder; +pub mod types; + +pub use self::backend::{Mysql, MysqlType}; +pub use self::connection::MysqlConnection; +pub use self::query_builder::MysqlQueryBuilder; +pub use self::value::{MysqlValue, NumericRepresentation}; diff --git a/collector/benchmarks/diesel/diesel/src/mysql/query_builder/limit_offset.rs b/collector/benchmarks/diesel/diesel/src/mysql/query_builder/limit_offset.rs new file mode 100644 index 000000000..2ddb0ae3f --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/query_builder/limit_offset.rs @@ -0,0 +1,104 @@ +use crate::mysql::Mysql; +use crate::query_builder::limit_clause::{LimitClause, NoLimitClause}; +use crate::query_builder::limit_offset_clause::{BoxedLimitOffsetClause, LimitOffsetClause}; +use crate::query_builder::offset_clause::{NoOffsetClause, OffsetClause}; +use crate::query_builder::{AstPass, IntoBoxedClause, QueryFragment}; +use crate::result::QueryResult; + +impl QueryFragment for LimitOffsetClause { + fn walk_ast(&self, _out: AstPass) -> QueryResult<()> { + Ok(()) + } +} + +impl QueryFragment for LimitOffsetClause, NoOffsetClause> +where + LimitClause: QueryFragment, +{ + fn walk_ast(&self, out: AstPass) -> QueryResult<()> { + self.limit_clause.walk_ast(out)?; + Ok(()) + } +} + +impl QueryFragment for LimitOffsetClause, OffsetClause> +where + LimitClause: QueryFragment, + OffsetClause: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.limit_clause.walk_ast(out.reborrow())?; + self.offset_clause.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl<'a> QueryFragment for BoxedLimitOffsetClause<'a, Mysql> { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + match (self.limit.as_ref(), self.offset.as_ref()) { + (Some(limit), Some(offset)) => { + limit.walk_ast(out.reborrow())?; + offset.walk_ast(out.reborrow())?; + } + (Some(limit), None) => { + limit.walk_ast(out.reborrow())?; + } + (None, Some(offset)) => { + // Mysql requires a limit clause in front of any offset clause + // The documentation proposes the following: + // > To retrieve all rows from a certain offset up to the end of the + // > result set, you can use some large number for the second parameter. + // https://dev.mysql.com/doc/refman/8.0/en/select.html + // Therefore we just use u64::MAX as limit here + // That does not result in any limitations because mysql only supports + // up to 64TB of data per table. Assuming 1 bit per row this means + // 1024 * 1024 * 1024 * 1024 * 8 = 562.949.953.421.312 rows which is smaller + // than 2^64 = 18.446.744.073.709.551.615 + out.push_sql(" LIMIT 18446744073709551615 "); + offset.walk_ast(out.reborrow())?; + } + (None, None) => {} + } + Ok(()) + } +} + +impl<'a> IntoBoxedClause<'a, Mysql> for LimitOffsetClause { + type BoxedClause = BoxedLimitOffsetClause<'a, Mysql>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: None, + offset: None, + } + } +} + +impl<'a, L> IntoBoxedClause<'a, Mysql> for LimitOffsetClause, NoOffsetClause> +where + L: QueryFragment + Send + 'a, +{ + type BoxedClause = BoxedLimitOffsetClause<'a, Mysql>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: Some(Box::new(self.limit_clause)), + offset: None, + } + } +} + +impl<'a, L, O> IntoBoxedClause<'a, Mysql> for LimitOffsetClause, OffsetClause> +where + L: QueryFragment + Send + 'a, + O: QueryFragment + Send + 'a, +{ + type BoxedClause = BoxedLimitOffsetClause<'a, Mysql>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: Some(Box::new(self.limit_clause)), + offset: Some(Box::new(self.offset_clause)), + } + } +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/query_builder/mod.rs b/collector/benchmarks/diesel/diesel/src/mysql/query_builder/mod.rs new file mode 100644 index 000000000..37d73cc92 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/query_builder/mod.rs @@ -0,0 +1,41 @@ +use super::backend::Mysql; +use crate::query_builder::QueryBuilder; +use crate::result::QueryResult; + +mod limit_offset; +mod query_fragment_impls; + +/// The MySQL query builder +#[allow(missing_debug_implementations)] +#[derive(Default)] +pub struct MysqlQueryBuilder { + sql: String, +} + +impl MysqlQueryBuilder { + /// Constructs a new query builder with an empty query + pub fn new() -> Self { + MysqlQueryBuilder::default() + } +} + +impl QueryBuilder for MysqlQueryBuilder { + fn push_sql(&mut self, sql: &str) { + self.sql.push_str(sql); + } + + fn push_identifier(&mut self, identifier: &str) -> QueryResult<()> { + self.push_sql("`"); + self.push_sql(&identifier.replace("`", "``")); + self.push_sql("`"); + Ok(()) + } + + fn push_bind_param(&mut self) { + self.push_sql("?"); + } + + fn finish(self) -> String { + self.sql + } +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/query_builder/query_fragment_impls.rs b/collector/benchmarks/diesel/diesel/src/mysql/query_builder/query_fragment_impls.rs new file mode 100644 index 000000000..fe3e6fdf3 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/query_builder/query_fragment_impls.rs @@ -0,0 +1,38 @@ +use crate::mysql::Mysql; +use crate::query_builder::locking_clause::{ForShare, ForUpdate, NoModifier, NoWait, SkipLocked}; +use crate::query_builder::{AstPass, QueryFragment}; +use crate::result::QueryResult; + +impl QueryFragment for ForUpdate { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" FOR UPDATE"); + Ok(()) + } +} + +impl QueryFragment for ForShare { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" FOR SHARE"); + Ok(()) + } +} + +impl QueryFragment for NoModifier { + fn walk_ast(&self, _out: AstPass) -> QueryResult<()> { + Ok(()) + } +} + +impl QueryFragment for SkipLocked { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" SKIP LOCKED"); + Ok(()) + } +} + +impl QueryFragment for NoWait { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" NOWAIT"); + Ok(()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/types/date_and_time.rs b/collector/benchmarks/diesel/diesel/src/mysql/types/date_and_time.rs new file mode 100644 index 000000000..093dd49fe --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/types/date_and_time.rs @@ -0,0 +1,281 @@ +use chrono::*; +use mysqlclient_sys as ffi; +use std::io::Write; +use std::os::raw as libc; +use std::{mem, slice}; + +use super::MYSQL_TIME; +use crate::deserialize::{self, FromSql}; +use crate::mysql::{Mysql, MysqlValue}; +use crate::serialize::{self, IsNull, Output, ToSql}; +use crate::sql_types::{Date, Datetime, Time, Timestamp}; + +macro_rules! mysql_time_impls { + ($ty:ty) => { + impl ToSql<$ty, Mysql> for MYSQL_TIME { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let bytes = unsafe { + let bytes_ptr = self as *const MYSQL_TIME as *const u8; + slice::from_raw_parts(bytes_ptr, mem::size_of::()) + }; + out.write_all(bytes)?; + Ok(IsNull::No) + } + } + + impl FromSql<$ty, Mysql> for MYSQL_TIME { + fn from_sql(value: MysqlValue<'_>) -> deserialize::Result { + value.time_value() + } + } + }; +} + +mysql_time_impls!(Datetime); +mysql_time_impls!(Timestamp); +mysql_time_impls!(Time); +mysql_time_impls!(Date); + +impl ToSql for NaiveDateTime { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + >::to_sql(self, out) + } +} + +impl FromSql for NaiveDateTime { + fn from_sql(bytes: MysqlValue<'_>) -> deserialize::Result { + >::from_sql(bytes) + } +} + +impl ToSql for NaiveDateTime { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let mysql_time = MYSQL_TIME { + year: self.year() as libc::c_uint, + month: self.month() as libc::c_uint, + day: self.day() as libc::c_uint, + hour: self.hour() as libc::c_uint, + minute: self.minute() as libc::c_uint, + second: self.second() as libc::c_uint, + second_part: libc::c_ulong::from(self.timestamp_subsec_micros()), + neg: false, + time_type: ffi::enum_mysql_timestamp_type::MYSQL_TIMESTAMP_DATETIME, + time_zone_displacement: 0, + }; + + >::to_sql(&mysql_time, out) + } +} + +impl FromSql for NaiveDateTime { + fn from_sql(bytes: MysqlValue<'_>) -> deserialize::Result { + let mysql_time = >::from_sql(bytes)?; + + NaiveDate::from_ymd_opt( + mysql_time.year as i32, + mysql_time.month as u32, + mysql_time.day as u32, + ) + .and_then(|v| { + v.and_hms_micro_opt( + mysql_time.hour as u32, + mysql_time.minute as u32, + mysql_time.second as u32, + mysql_time.second_part as u32, + ) + }) + .ok_or_else(|| format!("Cannot parse this date: {:?}", mysql_time).into()) + } +} + +impl ToSql for NaiveTime { + fn to_sql(&self, out: &mut serialize::Output) -> serialize::Result { + let mysql_time = MYSQL_TIME { + hour: self.hour() as libc::c_uint, + minute: self.minute() as libc::c_uint, + second: self.second() as libc::c_uint, + day: 0, + month: 0, + second_part: 0, + year: 0, + neg: false, + time_type: ffi::enum_mysql_timestamp_type::MYSQL_TIMESTAMP_TIME, + time_zone_displacement: 0, + }; + + >::to_sql(&mysql_time, out) + } +} + +impl FromSql for NaiveTime { + fn from_sql(bytes: MysqlValue<'_>) -> deserialize::Result { + let mysql_time = >::from_sql(bytes)?; + NaiveTime::from_hms_opt( + mysql_time.hour as u32, + mysql_time.minute as u32, + mysql_time.second as u32, + ) + .ok_or_else(|| format!("Unable to convert {:?} to chrono", mysql_time).into()) + } +} + +impl ToSql for NaiveDate { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let mysql_time = MYSQL_TIME { + year: self.year() as libc::c_uint, + month: self.month() as libc::c_uint, + day: self.day() as libc::c_uint, + hour: 0, + minute: 0, + second: 0, + second_part: 0, + neg: false, + time_type: ffi::enum_mysql_timestamp_type::MYSQL_TIMESTAMP_DATE, + time_zone_displacement: 0, + }; + + >::to_sql(&mysql_time, out) + } +} + +impl FromSql for NaiveDate { + fn from_sql(bytes: MysqlValue<'_>) -> deserialize::Result { + let mysql_time = >::from_sql(bytes)?; + NaiveDate::from_ymd_opt( + mysql_time.year as i32, + mysql_time.month as u32, + mysql_time.day as u32, + ) + .ok_or_else(|| format!("Unable to convert {:?} to chrono", mysql_time).into()) + } +} + +#[cfg(test)] +mod tests { + extern crate chrono; + extern crate dotenv; + + use self::chrono::{Duration, NaiveDate, NaiveTime, Utc}; + use self::dotenv::dotenv; + + use crate::dsl::{now, sql}; + use crate::prelude::*; + use crate::select; + use crate::sql_types::{Date, Datetime, Time, Timestamp}; + + fn connection() -> MysqlConnection { + dotenv().ok(); + + let connection_url = ::std::env::var("MYSQL_UNIT_TEST_DATABASE_URL") + .or_else(|_| ::std::env::var("MYSQL_DATABASE_URL")) + .or_else(|_| ::std::env::var("DATABASE_URL")) + .expect("DATABASE_URL must be set in order to run tests"); + MysqlConnection::establish(&connection_url).unwrap() + } + + #[test] + fn unix_epoch_encodes_correctly() { + let connection = connection(); + let time = NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0); + let query = select(sql::("CAST('1970-01-01' AS DATETIME)").eq(time)); + assert!(query.get_result::(&connection).unwrap()); + let query = select(sql::("CAST('1970-01-01' AS DATETIME)").eq(time)); + assert!(query.get_result::(&connection).unwrap()); + } + + #[test] + fn unix_epoch_decodes_correctly() { + let connection = connection(); + let time = NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0); + let epoch_from_sql = + select(sql::("CAST('1970-01-01' AS DATETIME)")).get_result(&connection); + assert_eq!(Ok(time), epoch_from_sql); + let epoch_from_sql = + select(sql::("CAST('1970-01-01' AS DATETIME)")).get_result(&connection); + assert_eq!(Ok(time), epoch_from_sql); + } + + #[test] + fn times_relative_to_now_encode_correctly() { + let connection = connection(); + let time = Utc::now().naive_utc() + Duration::days(1); + let query = select(now.lt(time)); + assert!(query.get_result::(&connection).unwrap()); + + let time = Utc::now().naive_utc() - Duration::days(1); + let query = select(now.gt(time)); + assert!(query.get_result::(&connection).unwrap()); + } + + #[test] + fn times_of_day_encode_correctly() { + let connection = connection(); + + let midnight = NaiveTime::from_hms(0, 0, 0); + let query = select(sql::
for ConflictTarget> {} diff --git a/collector/benchmarks/diesel/diesel/src/pg/query_builder/query_fragment_impls.rs b/collector/benchmarks/diesel/diesel/src/pg/query_builder/query_fragment_impls.rs new file mode 100644 index 000000000..e38745b9e --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/pg/query_builder/query_fragment_impls.rs @@ -0,0 +1,54 @@ +use crate::pg::Pg; +use crate::query_builder::locking_clause::{ + ForKeyShare, ForNoKeyUpdate, ForShare, ForUpdate, NoModifier, NoWait, SkipLocked, +}; +use crate::query_builder::{AstPass, QueryFragment}; +use crate::result::QueryResult; + +impl QueryFragment for ForUpdate { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" FOR UPDATE"); + Ok(()) + } +} + +impl QueryFragment for ForNoKeyUpdate { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" FOR NO KEY UPDATE"); + Ok(()) + } +} + +impl QueryFragment for ForShare { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" FOR SHARE"); + Ok(()) + } +} + +impl QueryFragment for ForKeyShare { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" FOR KEY SHARE"); + Ok(()) + } +} + +impl QueryFragment for NoModifier { + fn walk_ast(&self, _out: AstPass) -> QueryResult<()> { + Ok(()) + } +} + +impl QueryFragment for SkipLocked { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" SKIP LOCKED"); + Ok(()) + } +} + +impl QueryFragment for NoWait { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" NOWAIT"); + Ok(()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/pg/serialize/mod.rs b/collector/benchmarks/diesel/diesel/src/pg/serialize/mod.rs new file mode 100644 index 000000000..6bc1f4bbf --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/pg/serialize/mod.rs @@ -0,0 +1,3 @@ +mod write_tuple; + +pub use self::write_tuple::WriteTuple; diff --git a/collector/benchmarks/diesel/diesel/src/pg/serialize/write_tuple.rs b/collector/benchmarks/diesel/diesel/src/pg/serialize/write_tuple.rs new file mode 100644 index 000000000..5c2f55cdc --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/pg/serialize/write_tuple.rs @@ -0,0 +1,47 @@ +use std::io::Write; + +use crate::pg::Pg; +use crate::serialize::{self, Output}; + +/// Helper trait for writing tuples as named composite types +/// +/// This trait is essentially `ToSql>` for tuples. +/// While we can provide a valid body of `to_sql`, +/// PostgreSQL doesn't allow the use of bind parameters for unnamed composite types. +/// For this reason, we avoid implementing `ToSql` directly. +/// +/// This trait can be used by `ToSql` impls of named composite types. +/// +/// # Example +/// +/// ``` +/// # #[cfg(feature = "postgres")] +/// # mod the_impl { +/// # use diesel::prelude::*; +/// # use diesel::pg::Pg; +/// # use diesel::serialize::{self, ToSql, Output, WriteTuple}; +/// # use diesel::sql_types::{Integer, Text, SqlType}; +/// # use std::io::Write; +/// # +/// #[derive(SqlType)] +/// #[postgres(type_name = "my_type")] +/// struct MyType; +/// +/// #[derive(Debug)] +/// struct MyStruct<'a>(i32, &'a str); +/// +/// impl<'a> ToSql for MyStruct<'a> { +/// fn to_sql(&self, out: &mut Output) -> serialize::Result { +/// WriteTuple::<(Integer, Text)>::write_tuple( +/// &(self.0, self.1), +/// out, +/// ) +/// } +/// } +/// # } +/// # fn main() {} +/// ``` +pub trait WriteTuple { + /// See trait documentation. + fn write_tuple(&self, out: &mut Output) -> serialize::Result; +} diff --git a/collector/benchmarks/diesel/diesel/src/pg/transaction.rs b/collector/benchmarks/diesel/diesel/src/pg/transaction.rs new file mode 100644 index 000000000..fc49a2f06 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/pg/transaction.rs @@ -0,0 +1,401 @@ +#![allow(dead_code)] +use crate::backend::Backend; +use crate::connection::TransactionManager; +use crate::pg::Pg; +use crate::prelude::*; +use crate::query_builder::{AstPass, QueryBuilder, QueryFragment}; +use crate::result::Error; + +/// Used to build a transaction, specifying additional details. +/// +/// This struct is returned by [`.build_transaction`]. +/// See the documentation for methods on this struct for usage examples. +/// See [the PostgreSQL documentation for `SET TRANSACTION`][pg-docs] +/// for details on the behavior of each option. +/// +/// [`.build_transaction`]: struct.PgConnection.html#method.build_transaction +/// [pg-docs]: https://www.postgresql.org/docs/current/static/sql-set-transaction.html +#[allow(missing_debug_implementations)] // False positive. Connection isn't Debug. +#[derive(Clone, Copy)] +#[must_use = "Transaction builder does nothing unless you call `run` on it"] +pub struct TransactionBuilder<'a> { + connection: &'a PgConnection, + isolation_level: Option, + read_mode: Option, + deferrable: Option, +} + +impl<'a> TransactionBuilder<'a> { + pub(crate) fn new(connection: &'a PgConnection) -> Self { + Self { + connection, + isolation_level: None, + read_mode: None, + deferrable: None, + } + } + + /// Makes the transaction `READ ONLY` + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use diesel::sql_query; + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # table! { + /// # users_for_read_only { + /// # id -> Integer, + /// # name -> Text, + /// # } + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use users_for_read_only::table as users; + /// # use users_for_read_only::columns::*; + /// # let conn = connection_no_transaction(); + /// # sql_query("CREATE TABLE IF NOT EXISTS users_for_read_only ( + /// # id SERIAL PRIMARY KEY, + /// # name TEXT NOT NULL + /// # )").execute(&conn)?; + /// conn.build_transaction() + /// .read_only() + /// .run::<_, diesel::result::Error, _>(|| { + /// let read_attempt = users.select(name).load::(&conn); + /// assert!(read_attempt.is_ok()); + /// + /// let write_attempt = diesel::insert_into(users) + /// .values(name.eq("Ruby")) + /// .execute(&conn); + /// assert!(write_attempt.is_err()); + /// + /// Ok(()) + /// })?; + /// # sql_query("DROP TABLE users_for_read_only").execute(&conn)?; + /// # Ok(()) + /// # } + /// ``` + pub fn read_only(mut self) -> Self { + self.read_mode = Some(ReadMode::ReadOnly); + self + } + + /// Makes the transaction `READ WRITE` + /// + /// This is the default, unless you've changed the + /// `default_transaction_read_only` configuration parameter. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use diesel::result::Error::RollbackTransaction; + /// # use diesel::sql_query; + /// # + /// # fn main() { + /// # assert_eq!(run_test(), Err(RollbackTransaction)); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = connection_no_transaction(); + /// conn.build_transaction() + /// .read_write() + /// .run(|| { + /// # sql_query("CREATE TABLE IF NOT EXISTS users ( + /// # id SERIAL PRIMARY KEY, + /// # name TEXT NOT NULL + /// # )").execute(&conn)?; + /// let read_attempt = users.select(name).load::(&conn); + /// assert!(read_attempt.is_ok()); + /// + /// let write_attempt = diesel::insert_into(users) + /// .values(name.eq("Ruby")) + /// .execute(&conn); + /// assert!(write_attempt.is_ok()); + /// + /// # Err(RollbackTransaction) + /// # /* + /// Ok(()) + /// # */ + /// }) + /// # } + /// ``` + pub fn read_write(mut self) -> Self { + self.read_mode = Some(ReadMode::ReadWrite); + self + } + + /// Makes the transaction `DEFERRABLE` + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = connection_no_transaction(); + /// conn.build_transaction() + /// .deferrable() + /// .run(|| Ok(())) + /// # } + /// ``` + pub fn deferrable(mut self) -> Self { + self.deferrable = Some(Deferrable::Deferrable); + self + } + + /// Makes the transaction `NOT DEFERRABLE` + /// + /// This is the default, unless you've changed the + /// `default_transaction_deferrable` configuration parameter. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = connection_no_transaction(); + /// conn.build_transaction() + /// .not_deferrable() + /// .run(|| Ok(())) + /// # } + /// ``` + pub fn not_deferrable(mut self) -> Self { + self.deferrable = Some(Deferrable::NotDeferrable); + self + } + + /// Makes the transaction `ISOLATION LEVEL READ COMMITTED` + /// + /// This is the default, unless you've changed the + /// `default_transaction_isolation_level` configuration parameter. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = connection_no_transaction(); + /// conn.build_transaction() + /// .read_committed() + /// .run(|| Ok(())) + /// # } + /// ``` + pub fn read_committed(mut self) -> Self { + self.isolation_level = Some(IsolationLevel::ReadCommitted); + self + } + + /// Makes the transaction `ISOLATION LEVEL REPEATABLE READ` + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = connection_no_transaction(); + /// conn.build_transaction() + /// .repeatable_read() + /// .run(|| Ok(())) + /// # } + /// ``` + pub fn repeatable_read(mut self) -> Self { + self.isolation_level = Some(IsolationLevel::RepeatableRead); + self + } + + /// Makes the transaction `ISOLATION LEVEL SERIALIZABLE` + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = connection_no_transaction(); + /// conn.build_transaction() + /// .serializable() + /// .run(|| Ok(())) + /// # } + /// ``` + pub fn serializable(mut self) -> Self { + self.isolation_level = Some(IsolationLevel::Serializable); + self + } + + /// Runs the given function inside of the transaction + /// with the parameters given to this builder. + /// + /// Returns an error if the connection is already inside a transaction, + /// or if the transaction fails to commit or rollback + /// + /// If the transaction fails to commit due to a `SerializationFailure` or a + /// `ReadOnlyTransaction` a rollback will be attempted. If the rollback succeeds, + /// the original error will be returned, otherwise the error generated by the rollback + /// will be returned. In the second case the connection should be considered broken + /// as it contains a uncommitted unabortable open transaction. + pub fn run(&self, f: F) -> Result + where + F: FnOnce() -> Result, + E: From, + { + let mut query_builder = ::QueryBuilder::default(); + self.to_sql(&mut query_builder)?; + let sql = query_builder.finish(); + let transaction_manager = self.connection.transaction_manager(); + + transaction_manager.begin_transaction_sql(self.connection, &sql)?; + match f() { + Ok(value) => { + transaction_manager.commit_transaction(self.connection)?; + Ok(value) + } + Err(e) => { + transaction_manager.rollback_transaction(self.connection)?; + Err(e) + } + } + } +} + +impl<'a> QueryFragment for TransactionBuilder<'a> { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("BEGIN TRANSACTION"); + if let Some(ref isolation_level) = self.isolation_level { + isolation_level.walk_ast(out.reborrow())?; + } + if let Some(ref read_mode) = self.read_mode { + read_mode.walk_ast(out.reborrow())?; + } + if let Some(ref deferrable) = self.deferrable { + deferrable.walk_ast(out.reborrow())?; + } + Ok(()) + } +} + +#[derive(Debug, Clone, Copy)] +enum IsolationLevel { + ReadCommitted, + RepeatableRead, + Serializable, +} + +impl QueryFragment for IsolationLevel { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" ISOLATION LEVEL "); + match *self { + IsolationLevel::ReadCommitted => out.push_sql("READ COMMITTED"), + IsolationLevel::RepeatableRead => out.push_sql("REPEATABLE READ"), + IsolationLevel::Serializable => out.push_sql("SERIALIZABLE"), + } + Ok(()) + } +} + +#[derive(Debug, Clone, Copy)] +enum ReadMode { + ReadOnly, + ReadWrite, +} + +impl QueryFragment for ReadMode { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + match *self { + ReadMode::ReadOnly => out.push_sql(" READ ONLY"), + ReadMode::ReadWrite => out.push_sql(" READ WRITE"), + } + Ok(()) + } +} + +#[derive(Debug, Clone, Copy)] +enum Deferrable { + Deferrable, + NotDeferrable, +} + +impl QueryFragment for Deferrable { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + match *self { + Deferrable::Deferrable => out.push_sql(" DEFERRABLE"), + Deferrable::NotDeferrable => out.push_sql(" NOT DEFERRABLE"), + } + Ok(()) + } +} + +#[test] +fn test_transaction_builder_generates_correct_sql() { + extern crate dotenv; + + macro_rules! assert_sql { + ($query:expr, $sql:expr) => { + let mut query_builder = ::QueryBuilder::default(); + $query.to_sql(&mut query_builder).unwrap(); + let sql = query_builder.finish(); + assert_eq!(sql, $sql); + }; + } + + let database_url = dotenv::var("PG_DATABASE_URL") + .or_else(|_| dotenv::var("DATABASE_URL")) + .expect("DATABASE_URL must be set in order to run tests"); + let conn = PgConnection::establish(&database_url).unwrap(); + + let t = conn.build_transaction(); + assert_sql!(t, "BEGIN TRANSACTION"); + assert_sql!(t.read_only(), "BEGIN TRANSACTION READ ONLY"); + assert_sql!(t.read_write(), "BEGIN TRANSACTION READ WRITE"); + assert_sql!(t.deferrable(), "BEGIN TRANSACTION DEFERRABLE"); + assert_sql!(t.not_deferrable(), "BEGIN TRANSACTION NOT DEFERRABLE"); + assert_sql!( + t.read_committed(), + "BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED" + ); + assert_sql!( + t.repeatable_read(), + "BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ" + ); + assert_sql!( + t.serializable(), + "BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE" + ); + assert_sql!( + t.serializable().deferrable().read_only(), + "BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY DEFERRABLE" + ); +} diff --git a/collector/benchmarks/diesel/diesel/src/pg/types/array.rs b/collector/benchmarks/diesel/diesel/src/pg/types/array.rs new file mode 100644 index 000000000..7d799b325 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/pg/types/array.rs @@ -0,0 +1,143 @@ +use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt}; +use std::fmt; +use std::io::Write; + +use crate::deserialize::{self, FromSql}; +use crate::pg::{Pg, PgMetadataLookup, PgTypeMetadata, PgValue}; +use crate::serialize::{self, IsNull, Output, ToSql}; +use crate::sql_types::{Array, HasSqlType, Nullable}; + +impl HasSqlType> for Pg +where + Pg: HasSqlType, +{ + fn metadata(lookup: &PgMetadataLookup) -> PgTypeMetadata { + PgTypeMetadata { + oid: >::metadata(lookup).array_oid, + array_oid: 0, + } + } +} + +impl FromSql, Pg> for Vec +where + T: FromSql, +{ + fn from_sql(value: PgValue<'_>) -> deserialize::Result { + let mut bytes = value.as_bytes(); + let num_dimensions = bytes.read_i32::()?; + let has_null = bytes.read_i32::()? != 0; + let _oid = bytes.read_i32::()?; + + if num_dimensions == 0 { + return Ok(Vec::new()); + } + + let num_elements = bytes.read_i32::()?; + let _lower_bound = bytes.read_i32::()?; + + if num_dimensions != 1 { + return Err("multi-dimensional arrays are not supported".into()); + } + + (0..num_elements) + .map(|_| { + let elem_size = bytes.read_i32::()?; + if has_null && elem_size == -1 { + T::from_nullable_sql(None) + } else { + let (elem_bytes, new_bytes) = bytes.split_at(elem_size as usize); + bytes = new_bytes; + T::from_sql(PgValue::new(elem_bytes, value.get_oid())) + } + }) + .collect() + } +} + +use crate::expression::bound::Bound; +use crate::expression::AsExpression; + +macro_rules! array_as_expression { + ($ty:ty, $sql_type:ty) => { + impl<'a, 'b, ST, T> AsExpression<$sql_type> for $ty { + type Expression = Bound<$sql_type, Self>; + + fn as_expression(self) -> Self::Expression { + Bound::new(self) + } + } + }; +} + +array_as_expression!(&'a [T], Array); +array_as_expression!(&'a [T], Nullable>); +array_as_expression!(&'a &'b [T], Array); +array_as_expression!(&'a &'b [T], Nullable>); +array_as_expression!(Vec, Array); +array_as_expression!(Vec, Nullable>); +array_as_expression!(&'a Vec, Array); +array_as_expression!(&'a Vec, Nullable>); +array_as_expression!(&'a &'b Vec, Array); +array_as_expression!(&'a &'b Vec, Nullable>); + +impl ToSql, Pg> for [T] +where + Pg: HasSqlType, + T: ToSql, +{ + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let num_dimensions = 1; + out.write_i32::(num_dimensions)?; + let flags = 0; + out.write_i32::(flags)?; + let element_oid = Pg::metadata(out.metadata_lookup()).oid; + out.write_u32::(element_oid)?; + out.write_i32::(self.len() as i32)?; + let lower_bound = 1; + out.write_i32::(lower_bound)?; + + let mut buffer = out.with_buffer(Vec::new()); + for elem in self.iter() { + let is_null = elem.to_sql(&mut buffer)?; + if let IsNull::No = is_null { + out.write_i32::(buffer.len() as i32)?; + out.write_all(&buffer)?; + buffer.clear(); + } else { + // https://github.com/postgres/postgres/blob/82f8107b92c9104ec9d9465f3f6a4c6dab4c124a/src/backend/utils/adt/arrayfuncs.c#L1461 + out.write_i32::(-1)?; + } + } + + Ok(IsNull::No) + } +} + +impl ToSql>, Pg> for [T] +where + [T]: ToSql, Pg>, +{ + fn to_sql(&self, out: &mut Output) -> serialize::Result { + ToSql::, Pg>::to_sql(self, out) + } +} + +impl ToSql, Pg> for Vec +where + [T]: ToSql, Pg>, + T: fmt::Debug, +{ + fn to_sql(&self, out: &mut Output) -> serialize::Result { + (self as &[T]).to_sql(out) + } +} + +impl ToSql>, Pg> for Vec +where + Vec: ToSql, Pg>, +{ + fn to_sql(&self, out: &mut Output) -> serialize::Result { + ToSql::, Pg>::to_sql(self, out) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/pg/types/date_and_time/chrono.rs b/collector/benchmarks/diesel/diesel/src/pg/types/date_and_time/chrono.rs new file mode 100644 index 000000000..7d4d4c061 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/pg/types/date_and_time/chrono.rs @@ -0,0 +1,312 @@ +//! This module makes it possible to map `chrono::DateTime` values to postgres `Date` +//! and `Timestamp` fields. It is enabled with the `chrono` feature. + +extern crate chrono; + +use self::chrono::naive::MAX_DATE; +use self::chrono::{DateTime, Duration, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc}; +use std::io::Write; + +use super::{PgDate, PgTime, PgTimestamp}; +use crate::deserialize::{self, FromSql}; +use crate::pg::{Pg, PgValue}; +use crate::serialize::{self, Output, ToSql}; +use crate::sql_types::{Date, Time, Timestamp, Timestamptz}; + +// Postgres timestamps start from January 1st 2000. +fn pg_epoch() -> NaiveDateTime { + NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0) +} + +impl FromSql for NaiveDateTime { + fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { + let PgTimestamp(offset) = FromSql::::from_sql(bytes)?; + match pg_epoch().checked_add_signed(Duration::microseconds(offset)) { + Some(v) => Ok(v), + None => { + let message = "Tried to deserialize a timestamp that is too large for Chrono"; + Err(message.into()) + } + } + } +} + +impl ToSql for NaiveDateTime { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let time = match (self.signed_duration_since(pg_epoch())).num_microseconds() { + Some(time) => time, + None => { + let error_message = + format!("{:?} as microseconds is too large to fit in an i64", self); + return Err(error_message.into()); + } + }; + ToSql::::to_sql(&PgTimestamp(time), out) + } +} + +impl FromSql for NaiveDateTime { + fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { + FromSql::::from_sql(bytes) + } +} + +impl ToSql for NaiveDateTime { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + ToSql::::to_sql(self, out) + } +} + +impl FromSql for DateTime { + fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { + let naive_date_time = >::from_sql(bytes)?; + Ok(DateTime::from_utc(naive_date_time, Utc)) + } +} + +impl FromSql for DateTime { + fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { + let naive_date_time = >::from_sql(bytes)?; + Ok(Local::from_utc_datetime(&Local, &naive_date_time)) + } +} + +impl ToSql for DateTime { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + ToSql::::to_sql(&self.naive_utc(), out) + } +} + +fn midnight() -> NaiveTime { + NaiveTime::from_hms(0, 0, 0) +} + +impl ToSql for NaiveTime { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let duration = self.signed_duration_since(midnight()); + match duration.num_microseconds() { + Some(offset) => ToSql::::to_sql(&PgTime(offset), out), + None => unreachable!(), + } + } +} + +impl FromSql for NaiveTime { + fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { + let PgTime(offset) = FromSql::::from_sql(bytes)?; + let duration = Duration::microseconds(offset); + Ok(midnight() + duration) + } +} + +fn pg_epoch_date() -> NaiveDate { + NaiveDate::from_ymd(2000, 1, 1) +} + +impl ToSql for NaiveDate { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let days_since_epoch = self.signed_duration_since(pg_epoch_date()).num_days(); + ToSql::::to_sql(&PgDate(days_since_epoch as i32), out) + } +} + +impl FromSql for NaiveDate { + fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { + let PgDate(offset) = FromSql::::from_sql(bytes)?; + match pg_epoch_date().checked_add_signed(Duration::days(i64::from(offset))) { + Some(date) => Ok(date), + None => { + let error_message = format!("Chrono can only represent dates up to {:?}", MAX_DATE); + Err(error_message.into()) + } + } + } +} + +#[cfg(test)] +mod tests { + extern crate chrono; + extern crate dotenv; + + use self::chrono::naive::MAX_DATE; + use self::chrono::{Duration, FixedOffset, NaiveDate, NaiveTime, TimeZone, Utc}; + use self::dotenv::dotenv; + + use crate::dsl::{now, sql}; + use crate::prelude::*; + use crate::select; + use crate::sql_types::{Date, Time, Timestamp, Timestamptz}; + + fn connection() -> PgConnection { + dotenv().ok(); + + let connection_url = ::std::env::var("PG_DATABASE_URL") + .or_else(|_| ::std::env::var("DATABASE_URL")) + .expect("DATABASE_URL must be set in order to run tests"); + PgConnection::establish(&connection_url).unwrap() + } + + #[test] + fn unix_epoch_encodes_correctly() { + let connection = connection(); + let time = NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0); + let query = select(sql::("'1970-01-01'").eq(time)); + assert!(query.get_result::(&connection).unwrap()); + } + + #[test] + fn unix_epoch_encodes_correctly_with_utc_timezone() { + let connection = connection(); + let time = Utc.ymd(1970, 1, 1).and_hms(0, 0, 0); + let query = select(sql::("'1970-01-01Z'::timestamptz").eq(time)); + assert!(query.get_result::(&connection).unwrap()); + } + + #[test] + fn unix_epoch_encodes_correctly_with_timezone() { + let connection = connection(); + let time = FixedOffset::west(3600).ymd(1970, 1, 1).and_hms(0, 0, 0); + let query = select(sql::("'1970-01-01 01:00:00Z'::timestamptz").eq(time)); + assert!(query.get_result::(&connection).unwrap()); + } + + #[test] + fn unix_epoch_decodes_correctly() { + let connection = connection(); + let time = NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0); + let epoch_from_sql = + select(sql::("'1970-01-01'::timestamp")).get_result(&connection); + assert_eq!(Ok(time), epoch_from_sql); + } + + #[test] + fn unix_epoch_decodes_correctly_with_timezone() { + let connection = connection(); + let time = Utc.ymd(1970, 1, 1).and_hms(0, 0, 0); + let epoch_from_sql = + select(sql::("'1970-01-01Z'::timestamptz")).get_result(&connection); + assert_eq!(Ok(time), epoch_from_sql); + } + + #[test] + fn times_relative_to_now_encode_correctly() { + let connection = connection(); + let time = Utc::now().naive_utc() + Duration::seconds(60); + let query = select(now.at_time_zone("utc").lt(time)); + assert!(query.get_result::(&connection).unwrap()); + + let time = Utc::now().naive_utc() - Duration::seconds(60); + let query = select(now.at_time_zone("utc").gt(time)); + assert!(query.get_result::(&connection).unwrap()); + } + + #[test] + fn times_with_timezones_round_trip_after_conversion() { + let connection = connection(); + let time = FixedOffset::east(3600).ymd(2016, 1, 2).and_hms(1, 0, 0); + let expected = NaiveDate::from_ymd(2016, 1, 1).and_hms(20, 0, 0); + let query = select(time.into_sql::().at_time_zone("EDT")); + assert_eq!(Ok(expected), query.get_result(&connection)); + } + + #[test] + fn times_of_day_encode_correctly() { + let connection = connection(); + + let midnight = NaiveTime::from_hms(0, 0, 0); + let query = select(sql::
+ Expression, + U: Query, + { + InsertStatement::new( + self.target, + self.records.with_columns(columns), + self.operator, + self.returning, + ) + } +} + +impl QueryFragment for InsertStatement +where + DB: Backend, + T: Table, + T::FromClause: QueryFragment, + U: QueryFragment + CanInsertInSingleQuery, + Op: QueryFragment, + Ret: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + + if self.records.rows_to_insert() == Some(0) { + out.push_sql("SELECT 1 FROM "); + self.target.from_clause().walk_ast(out.reborrow())?; + out.push_sql(" WHERE 1=0"); + return Ok(()); + } + + self.operator.walk_ast(out.reborrow())?; + out.push_sql(" INTO "); + self.target.from_clause().walk_ast(out.reborrow())?; + out.push_sql(" "); + self.records.walk_ast(out.reborrow())?; + self.returning.walk_ast(out.reborrow())?; + Ok(()) + } +} + +#[cfg(feature = "sqlite")] +impl<'a, T, U, Op, C> ExecuteDsl for InsertStatement, Op> +where + C: Connection, + &'a U: Insertable, + InsertStatement>::Values, Op>: QueryFragment, + T: Copy, + Op: Copy, +{ + fn execute(query: Self, conn: &C) -> QueryResult { + conn.transaction(|| { + let mut result = 0; + for record in query.records.records { + result += InsertStatement::new( + query.target, + record.values(), + query.operator, + query.returning, + ) + .execute(conn)?; + } + Ok(result) + }) + } +} + +#[cfg(feature = "sqlite")] +impl<'a, T, U, Op> Display for DebugQuery<'a, InsertStatement, Op>, Sqlite> +where + &'a U: Insertable, + for<'b> DebugQuery<'b, InsertStatement>::Values, Op>, Sqlite>: + Display, + T: Copy, + Op: Copy, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f, "BEGIN;")?; + for record in self.query.records.records { + let stmt = InsertStatement::new( + self.query.target, + record.values(), + self.query.operator, + self.query.returning, + ); + + writeln!(f, "{}", crate::debug_query::(&stmt))?; + } + writeln!(f, "COMMIT;")?; + Ok(()) + } +} + +#[cfg(feature = "sqlite")] +impl<'a, T, U, Op> Debug for DebugQuery<'a, InsertStatement, Op>, Sqlite> +where + &'a U: Insertable, + for<'b> DebugQuery<'b, InsertStatement>::Values, Op>, Sqlite>: + Display, + T: Copy, + Op: Copy, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut statements = Vec::with_capacity(self.query.records.records.len() + 2); + statements.push("BEGIN".into()); + for record in self.query.records.records { + let stmt = InsertStatement::new( + self.query.target, + record.values(), + self.query.operator, + self.query.returning, + ); + statements.push(format!("{}", crate::debug_query::(&stmt))); + } + statements.push("COMMIT".into()); + + f.debug_struct("Query") + .field("sql", &statements) + .field("binds", &[] as &[i32; 0]) + .finish() + } +} + +#[cfg(feature = "sqlite")] +impl ExecuteDsl + for InsertStatement, T>, Op> +where + C: Connection, + InsertStatement, Op>: QueryFragment, + T: Copy, + Op: Copy, +{ + fn execute(query: Self, conn: &C) -> QueryResult { + conn.transaction(|| { + let mut result = 0; + for value in query.records.values { + result += + InsertStatement::new(query.target, value, query.operator, query.returning) + .execute(conn)?; + } + Ok(result) + }) + } +} + +#[cfg(feature = "sqlite")] +impl<'a, T, U, Op> Display + for DebugQuery<'a, InsertStatement, T>, Op>, Sqlite> +where + for<'b> DebugQuery<'b, InsertStatement, Op>, Sqlite>: Display, + T: Copy, + Op: Copy, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f, "BEGIN;")?; + for value in &self.query.records.values { + let stmt = InsertStatement::new( + self.query.target, + value, + self.query.operator, + self.query.returning, + ); + + writeln!(f, "{}", crate::debug_query::(&stmt))?; + } + writeln!(f, "COMMIT;")?; + Ok(()) + } +} + +#[cfg(feature = "sqlite")] +impl<'a, T, U, Op> Debug + for DebugQuery<'a, InsertStatement, T>, Op>, Sqlite> +where + for<'b> DebugQuery<'b, InsertStatement, Op>, Sqlite>: Display, + T: Copy, + Op: Copy, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut statements = Vec::with_capacity(self.query.records.values.len() + 2); + statements.push("BEGIN".into()); + + for value in &self.query.records.values { + let stmt = InsertStatement::new( + self.query.target, + value, + self.query.operator, + self.query.returning, + ); + statements.push(format!("{}", crate::debug_query::(&stmt))); + } + statements.push("COMMIT".into()); + + f.debug_struct("Query") + .field("sql", &statements) + .field("binds", &[] as &[i32; 0]) + .finish() + } +} + +impl QueryId for InsertStatement { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl AsQuery for InsertStatement +where + T: Table, + InsertStatement>: Query, +{ + type SqlType = ::SqlType; + type Query = InsertStatement>; + + fn as_query(self) -> Self::Query { + self.returning(T::all_columns()) + } +} + +impl Query for InsertStatement> +where + Ret: Expression + SelectableExpression + NonAggregate, +{ + type SqlType = Ret::SqlType; +} + +impl RunQueryDsl for InsertStatement {} + +impl InsertStatement { + /// Specify what expression is returned after execution of the `insert`. + /// # Examples + /// + /// ### Inserting records: + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # #[cfg(feature = "postgres")] + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let inserted_names = diesel::insert_into(users) + /// .values(&vec![name.eq("Timmy"), name.eq("Jimmy")]) + /// .returning(name) + /// .get_results(&connection); + /// assert_eq!(Ok(vec!["Timmy".to_string(), "Jimmy".to_string()]), inserted_names); + /// # } + /// # #[cfg(not(feature = "postgres"))] + /// # fn main() {} + /// ``` + pub fn returning(self, returns: E) -> InsertStatement> + where + InsertStatement>: Query, + { + InsertStatement::new( + self.target, + self.records, + self.operator, + ReturningClause(returns), + ) + } +} + +#[derive(Debug, Copy, Clone, QueryId)] +#[doc(hidden)] +pub struct Insert; + +impl QueryFragment for Insert { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("INSERT"); + Ok(()) + } +} + +#[derive(Debug, Copy, Clone, QueryId)] +#[doc(hidden)] +pub struct InsertOrIgnore; + +#[cfg(feature = "sqlite")] +impl QueryFragment for InsertOrIgnore { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("INSERT OR IGNORE"); + Ok(()) + } +} + +#[cfg(feature = "mysql")] +impl QueryFragment for InsertOrIgnore { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("INSERT IGNORE"); + Ok(()) + } +} + +#[derive(Debug, Copy, Clone)] +#[doc(hidden)] +pub struct Replace; + +#[cfg(feature = "sqlite")] +impl QueryFragment for Replace { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("REPLACE"); + Ok(()) + } +} + +#[cfg(feature = "mysql")] +impl QueryFragment for Replace { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("REPLACE"); + Ok(()) + } +} + +/// Marker trait to indicate that no additional operations have been added +/// to a record for insert. +/// +/// This is used to prevent things like +/// `.on_conflict_do_nothing().on_conflict_do_nothing()` +/// from compiling. +pub trait UndecoratedInsertRecord
{} + +impl<'a, T, Tab> UndecoratedInsertRecord for &'a T where + T: ?Sized + UndecoratedInsertRecord +{ +} + +impl UndecoratedInsertRecord for ColumnInsertValue where T: Column {} + +impl UndecoratedInsertRecord
for [T] where T: UndecoratedInsertRecord
{} + +impl<'a, T, Table> UndecoratedInsertRecord
for BatchInsert<'a, T, Table> where + T: UndecoratedInsertRecord
+{ +} + +impl UndecoratedInsertRecord
for OwnedBatchInsert where + T: UndecoratedInsertRecord
+{ +} + +impl UndecoratedInsertRecord
for Vec where [T]: UndecoratedInsertRecord
{} + +impl UndecoratedInsertRecord for Eq where Lhs: Column {} + +impl UndecoratedInsertRecord for Option> where + Eq: UndecoratedInsertRecord +{ +} + +impl UndecoratedInsertRecord for Grouped> where Lhs: Column {} + +impl UndecoratedInsertRecord for Option>> where + Eq: UndecoratedInsertRecord +{ +} + +impl UndecoratedInsertRecord
for ValuesClause where + T: UndecoratedInsertRecord
+{ +} + +#[derive(Debug, Clone, Copy)] +#[doc(hidden)] +pub struct DefaultValues; + +impl CanInsertInSingleQuery for DefaultValues { + fn rows_to_insert(&self) -> Option { + Some(1) + } +} + +impl<'a, Tab> Insertable for &'a DefaultValues { + type Values = DefaultValues; + + fn values(self) -> Self::Values { + *self + } +} + +impl QueryFragment for DefaultValues +where + DB: Backend + Any, +{ + #[cfg(feature = "mysql")] + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + // This can be less hacky once stabilization lands + if TypeId::of::() == TypeId::of::() { + out.push_sql("() VALUES ()"); + } else { + out.push_sql("DEFAULT VALUES"); + } + Ok(()) + } + + #[cfg(not(feature = "mysql"))] + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("DEFAULT VALUES"); + Ok(()) + } +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct ValuesClause { + pub values: T, + _marker: PhantomData, +} + +impl Default for ValuesClause { + fn default() -> Self { + Self::new(T::default()) + } +} + +impl ValuesClause { + pub(crate) fn new(values: T) -> Self { + Self { + values, + _marker: PhantomData, + } + } +} + +impl CanInsertInSingleQuery for ValuesClause +where + DB: Backend, + T: CanInsertInSingleQuery, +{ + fn rows_to_insert(&self) -> Option { + self.values.rows_to_insert() + } +} + +impl QueryFragment for ValuesClause +where + DB: Backend, + Tab: Table, + T: InsertValues, + DefaultValues: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + if self.values.is_noop()? { + DefaultValues.walk_ast(out)?; + } else { + out.push_sql("("); + self.values.column_names(out.reborrow())?; + out.push_sql(") VALUES ("); + self.values.walk_ast(out.reborrow())?; + out.push_sql(")"); + } + Ok(()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/limit_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/limit_clause.rs new file mode 100644 index 000000000..fd83de786 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/limit_clause.rs @@ -0,0 +1,11 @@ +simple_clause!( + /// A query node indicating the absence of a limit clause + /// + /// This type is only relevant for implementing custom backends + NoLimitClause, + /// A query node representing a limit clause + /// + /// This type is only relevant for implementing custom backends + LimitClause, + " LIMIT " +); diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/limit_offset_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/limit_offset_clause.rs new file mode 100644 index 000000000..e167284b7 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/limit_offset_clause.rs @@ -0,0 +1,24 @@ +use super::QueryFragment; +use crate::query_builder::QueryId; + +/// A helper query node that contains both limit and offset clauses +/// +/// This type is only relevant for implementing custom backends +#[derive(Debug, Clone, Copy, QueryId)] +pub struct LimitOffsetClause { + /// The limit clause + pub limit_clause: Limit, + /// The offset clause + pub offset_clause: Offset, +} + +/// A boxed variant of [`LimitOffsetClause`](../struct.LimitOffsetClause.html) +/// +/// This type is only relevant for implementing custom backends +#[allow(missing_debug_implementations)] +pub struct BoxedLimitOffsetClause<'a, DB> { + /// The limit clause + pub limit: Option + Send + 'a>>, + /// The offset clause + pub offset: Option + Send + 'a>>, +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/locking_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/locking_clause.rs new file mode 100644 index 000000000..8605ac930 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/locking_clause.rs @@ -0,0 +1,61 @@ +use crate::backend::Backend; +use crate::query_builder::{AstPass, QueryFragment, QueryId}; +use crate::result::QueryResult; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct NoLockingClause; + +impl QueryFragment for NoLockingClause { + fn walk_ast(&self, _: AstPass) -> QueryResult<()> { + Ok(()) + } +} + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct LockingClause { + pub(crate) lock_mode: LockMode, + modifier: Modifier, +} + +impl LockingClause { + pub(crate) fn new(lock_mode: LockMode, modifier: Modifier) -> Self { + LockingClause { + lock_mode, + modifier, + } + } +} + +impl, M: QueryFragment> QueryFragment + for LockingClause +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.lock_mode.walk_ast(out.reborrow())?; + self.modifier.walk_ast(out.reborrow()) + } +} + +// `LockMode` parameters +// All the different types of row locks that can be acquired. +#[derive(Debug, Clone, Copy, QueryId)] +pub struct ForUpdate; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct ForNoKeyUpdate; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct ForShare; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct ForKeyShare; + +// Modifiers +// To be used in conjunction with a lock mode. +#[derive(Debug, Clone, Copy, QueryId)] +pub struct NoModifier; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct SkipLocked; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct NoWait; diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/mod.rs b/collector/benchmarks/diesel/diesel/src/query_builder/mod.rs new file mode 100644 index 000000000..5c8f44dcb --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/mod.rs @@ -0,0 +1,336 @@ +//! Contains traits responsible for the actual construction of SQL statements +//! +//! The types in this module are part of Diesel's public API, but are generally +//! only useful for implementing Diesel plugins. Applications should generally +//! not need to care about the types inside of this module. + +#[macro_use] +mod query_id; +#[macro_use] +mod clause_macro; + +mod ast_pass; +pub mod bind_collector; +mod debug_query; +mod delete_statement; +mod distinct_clause; +#[doc(hidden)] +pub mod functions; +mod group_by_clause; +mod insert_statement; +pub(crate) mod limit_clause; +pub(crate) mod limit_offset_clause; +pub(crate) mod locking_clause; +#[doc(hidden)] +pub mod nodes; +pub(crate) mod offset_clause; +mod order_clause; +mod returning_clause; +pub(crate) mod select_clause; +mod select_statement; +mod sql_query; +mod update_statement; +pub(crate) mod upsert; +mod where_clause; + +pub use self::ast_pass::AstPass; +pub use self::bind_collector::BindCollector; +pub use self::debug_query::DebugQuery; +pub use self::delete_statement::{BoxedDeleteStatement, DeleteStatement}; +#[doc(inline)] +pub use self::insert_statement::{ + IncompleteInsertStatement, InsertStatement, UndecoratedInsertRecord, ValuesClause, +}; +pub use self::query_id::QueryId; +#[doc(inline)] +pub use self::select_clause::{ + IntoBoxedSelectClause, SelectClauseExpression, SelectClauseQueryFragment, +}; +#[doc(hidden)] +pub use self::select_statement::{BoxedSelectStatement, SelectStatement}; +pub use self::sql_query::{BoxedSqlQuery, SqlQuery}; +#[doc(inline)] +pub use self::update_statement::{ + AsChangeset, BoxedUpdateStatement, IntoUpdateTarget, UpdateStatement, UpdateTarget, +}; +pub use self::upsert::on_conflict_target_decorations::DecoratableTarget; + +pub use self::limit_clause::{LimitClause, NoLimitClause}; +pub use self::limit_offset_clause::{BoxedLimitOffsetClause, LimitOffsetClause}; +pub use self::offset_clause::{NoOffsetClause, OffsetClause}; + +pub(crate) use self::insert_statement::ColumnList; + +use std::error::Error; + +use crate::backend::Backend; +use crate::result::QueryResult; + +#[doc(hidden)] +pub type Binds = Vec>>; +/// A specialized Result type used with the query builder. +pub type BuildQueryResult = Result<(), Box>; + +/// Constructs a SQL query from a Diesel AST. +/// +/// The only reason you should ever need to interact with this trait is if you +/// are extending Diesel with support for a new backend. Plugins which extend +/// the query builder with new capabilities will interact with [`AstPass`] +/// instead. +/// +/// [`AstPass`]: struct.AstPass.html +pub trait QueryBuilder { + /// Add `sql` to the end of the query being constructed. + fn push_sql(&mut self, sql: &str); + + /// Quote `identifier`, and add it to the end of the query being + /// constructed. + fn push_identifier(&mut self, identifier: &str) -> QueryResult<()>; + + /// Add a placeholder for a bind parameter to the end of the query being + /// constructed. + fn push_bind_param(&mut self); + + /// Increases the internal counter for bind parameters without adding the + /// bind parameter itself to the query + fn push_bind_param_value_only(&mut self) {} + + /// Returns the constructed SQL query. + fn finish(self) -> String; +} + +/// A complete SQL query with a return type. +/// +/// This can be a select statement, or a command such as `update` or `insert` +/// with a `RETURNING` clause. Unlike [`Expression`], types implementing this +/// trait are guaranteed to be executable on their own. +/// +/// A type which doesn't implement this trait may still represent a complete SQL +/// query. For example, an `INSERT` statement without a `RETURNING` clause will +/// not implement this trait, but can still be executed. +/// +/// [`Expression`]: ../expression/trait.Expression.html +pub trait Query { + /// The SQL type that this query represents. + /// + /// This is the SQL type of the `SELECT` clause for select statements, and + /// the SQL type of the `RETURNING` clause for insert, update, or delete + /// statements. + type SqlType; +} + +impl<'a, T: Query> Query for &'a T { + type SqlType = T::SqlType; +} + +/// Indicates that a type is a `SELECT` statement. +/// +/// This trait differs from `Query` in two ways: +/// - It is implemented only for select statements, rather than all queries +/// which return a value. +/// - It has looser constraints. A type implementing `SelectQuery` is known to +/// be potentially valid if used as a subselect, but it is not necessarily +/// able to be executed. +pub trait SelectQuery { + /// The SQL type of the `SELECT` clause + type SqlType; +} + +/// An untyped fragment of SQL. +/// +/// This may be a complete SQL command (such as an update statement without a +/// `RETURNING` clause), or a subsection (such as our internal types used to +/// represent a `WHERE` clause). Implementations of [`ExecuteDsl`] and +/// [`LoadQuery`] will generally require that this trait be implemented. +/// +/// [`ExecuteDsl`]: ../query_dsl/methods/trait.ExecuteDsl.html +/// [`LoadQuery`]: ../query_dsl/methods/trait.LoadQuery.html +pub trait QueryFragment { + /// Walk over this `QueryFragment` for all passes. + /// + /// This method is where the actual behavior of an AST node is implemented. + /// This method will contain the behavior required for all possible AST + /// passes. See [`AstPass`] for more details. + /// + /// [`AstPass`]: struct.AstPass.html + fn walk_ast(&self, pass: AstPass) -> QueryResult<()>; + + /// Converts this `QueryFragment` to its SQL representation. + /// + /// This method should only be called by implementations of `Connection`. + fn to_sql(&self, out: &mut DB::QueryBuilder) -> QueryResult<()> { + self.walk_ast(AstPass::to_sql(out)) + } + + /// Serializes all bind parameters in this query. + /// + /// A bind parameter is a value which is sent separately from the query + /// itself. It is represented in SQL with a placeholder such as `?` or `$1`. + /// + /// This method should only be called by implementations of `Connection`. + fn collect_binds( + &self, + out: &mut DB::BindCollector, + metadata_lookup: &DB::MetadataLookup, + ) -> QueryResult<()> { + self.walk_ast(AstPass::collect_binds(out, metadata_lookup)) + } + + /// Is this query safe to store in the prepared statement cache? + /// + /// In order to keep our prepared statement cache at a reasonable size, we + /// avoid caching any queries which represent a potentially unbounded number + /// of SQL queries. Generally this will only return `true` for queries for + /// which `to_sql` will always construct exactly identical SQL. + /// + /// Some examples of where this method will return `false` are: + /// + /// - `SqlLiteral` (We don't know if the SQL was constructed dynamically, so + /// we must assume that it was) + /// - `In` and `NotIn` (Each value requires a separate bind param + /// placeholder) + /// + /// This method should only be called by implementations of `Connection`. + fn is_safe_to_cache_prepared(&self) -> QueryResult { + let mut result = true; + self.walk_ast(AstPass::is_safe_to_cache_prepared(&mut result))?; + Ok(result) + } + + #[doc(hidden)] + /// Does walking this AST have any effect? + fn is_noop(&self) -> QueryResult { + let mut result = true; + self.walk_ast(AstPass::is_noop(&mut result))?; + Ok(result) + } +} + +impl QueryFragment for Box +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + QueryFragment::walk_ast(&**self, pass) + } +} + +impl<'a, T: ?Sized, DB> QueryFragment for &'a T +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + QueryFragment::walk_ast(&**self, pass) + } +} + +impl QueryFragment for () { + fn walk_ast(&self, _: AstPass) -> QueryResult<()> { + Ok(()) + } +} + +impl QueryFragment for Option +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, out: AstPass) -> QueryResult<()> { + match *self { + Some(ref c) => c.walk_ast(out), + None => Ok(()), + } + } +} + +/// A trait used to construct type erased boxed variant of the current query node +/// +/// Mainly useful for implementing third party backends +pub trait IntoBoxedClause<'a, DB> { + /// Resulting type + type BoxedClause; + + /// Convert the given query node in it's boxed representation + fn into_boxed(self) -> Self::BoxedClause; +} + +/// Types that can be converted into a complete, typed SQL query. +/// +/// This is used internally to automatically add the right select clause when +/// none is specified, or to automatically add `RETURNING *` in certain contexts. +/// +/// A type which implements this trait is guaranteed to be valid for execution. +pub trait AsQuery { + /// The SQL type of `Self::Query` + type SqlType; + + /// What kind of query does this type represent? + type Query: Query; + + /// Converts a type which semantically represents a SQL query into the + /// actual query being executed. See the trait level docs for more. + fn as_query(self) -> Self::Query; +} + +impl AsQuery for T { + type SqlType = ::SqlType; + type Query = Self; + + fn as_query(self) -> Self::Query { + self + } +} + +/// Takes a query `QueryFragment` expression as an argument and returns a type +/// that implements `fmt::Display` and `fmt::Debug` to show the query. +/// +/// The `Display` implementation will show the exact query being sent to the +/// server, with a comment showing the values of the bind parameters. The +/// `Debug` implementation will include the same information in a more +/// structured form, and respects pretty printing. +/// +/// # Example +/// +/// ### Returning SQL from a count statement: +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # +/// # use diesel::*; +/// # use schema::*; +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// let sql = debug_query::(&users.count()).to_string(); +/// # if cfg!(feature = "postgres") { +/// # assert_eq!(sql, r#"SELECT COUNT(*) FROM "users" -- binds: []"#); +/// # } else { +/// assert_eq!(sql, "SELECT COUNT(*) FROM `users` -- binds: []"); +/// # } +/// +/// let query = users.find(1); +/// let debug = debug_query::(&query); +/// # if cfg!(feature = "postgres") { +/// # assert_eq!(debug.to_string(), "SELECT \"users\".\"id\", \"users\".\"name\" \ +/// # FROM \"users\" WHERE (\"users\".\"id\" = $1) -- binds: [1]"); +/// # } else { +/// assert_eq!(debug.to_string(), "SELECT `users`.`id`, `users`.`name` FROM `users` \ +/// WHERE (`users`.`id` = ?) -- binds: [1]"); +/// # } +/// +/// let debug = format!("{:?}", debug); +/// # if !cfg!(feature = "postgres") { // Escaping that string is a pain +/// let expected = "Query { \ +/// sql: \"SELECT `users`.`id`, `users`.`name` FROM `users` WHERE \ +/// (`users`.`id` = ?)\", \ +/// binds: [1] \ +/// }"; +/// assert_eq!(debug, expected); +/// # } +/// # } +/// ``` +pub fn debug_query(query: &T) -> DebugQuery { + DebugQuery::new(query) +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/nodes/mod.rs b/collector/benchmarks/diesel/diesel/src/query_builder/nodes/mod.rs new file mode 100644 index 000000000..7a1787095 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/nodes/mod.rs @@ -0,0 +1,43 @@ +use crate::backend::Backend; +use crate::query_builder::*; +use crate::result::QueryResult; + +#[derive(Debug, Copy, Clone)] +pub struct Identifier<'a>(pub &'a str); + +impl<'a, DB: Backend> QueryFragment for Identifier<'a> { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_identifier(self.0) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct InfixNode<'a, T, U> { + lhs: T, + rhs: U, + middle: &'a str, +} + +impl<'a, T, U> InfixNode<'a, T, U> { + pub fn new(lhs: T, rhs: U, middle: &'a str) -> Self { + InfixNode { + lhs: lhs, + rhs: rhs, + middle: middle, + } + } +} + +impl<'a, T, U, DB> QueryFragment for InfixNode<'a, T, U> +where + DB: Backend, + T: QueryFragment, + U: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.lhs.walk_ast(out.reborrow())?; + out.push_sql(self.middle); + self.rhs.walk_ast(out.reborrow())?; + Ok(()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/offset_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/offset_clause.rs new file mode 100644 index 000000000..3cb72e09f --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/offset_clause.rs @@ -0,0 +1,11 @@ +simple_clause!( + /// A query node indicating the absence of an offset clause + /// + /// This type is only relevant for implementing custom backends + NoOffsetClause, + /// A query node representing an offset clause + /// + /// This type is only relevant for implementing custom backends + OffsetClause, + " OFFSET " +); diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/order_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/order_clause.rs new file mode 100644 index 000000000..deea8dc71 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/order_clause.rs @@ -0,0 +1,20 @@ +simple_clause!(NoOrderClause, OrderClause, " ORDER BY "); + +impl<'a, DB, Expr> Into + Send + 'a>>> for OrderClause +where + DB: Backend, + Expr: QueryFragment + Send + 'a, +{ + fn into(self) -> Option + Send + 'a>> { + Some(Box::new(self.0)) + } +} + +impl<'a, DB> Into + Send + 'a>>> for NoOrderClause +where + DB: Backend, +{ + fn into(self) -> Option + Send + 'a>> { + None + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/query_id.rs b/collector/benchmarks/diesel/diesel/src/query_builder/query_id.rs new file mode 100644 index 000000000..aa2ccb957 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/query_id.rs @@ -0,0 +1,144 @@ +use super::QueryFragment; +use std::any::{Any, TypeId}; + +/// Uniquely identifies queries by their type for the purpose of prepared +/// statement caching. +/// +/// All types which implement `QueryFragment` should also implement this trait +/// (It is not an actual supertrait of `QueryFragment` for boxing purposes). +/// +/// See the documentation of [the `QueryId` type] and [`HAS_STATIC_QUERY_ID`] +/// for more details. +/// +/// [the `QueryId` type]: #associatedtype.QueryId +/// [`HAS_STATIC_QUERY_ID`]: #associatedconstant.HAS_STATIC_QUERY_ID +/// +/// ### Deriving +/// +/// This trait can [be automatically derived](derive.QueryId.html) +/// by Diesel. +/// For example, given this struct: +/// +/// If the SQL generated by a struct is not uniquely identifiable by its type, +/// meaning that `HAS_STATIC_QUERY_ID` should always be false, +/// you should not derive this trait. +/// In that case you should manually implement it instead. +pub trait QueryId { + /// A type which uniquely represents `Self` in a SQL query. + /// + /// Typically this will be a re-construction of `Self` using the `QueryId` + /// type of each of your type parameters. For example, the type `And` would have `type QueryId = And`. + /// + /// The exception to this is when one of your type parameters does not + /// affect whether the same prepared statement can be used or not. For + /// example, a bind parameter is represented as `Bound`. + /// The actual Rust type we are serializing does not matter for the purposes + /// of prepared statement reuse, but a query which has identical SQL but + /// different types for its bind parameters requires a new prepared + /// statement. For this reason, `Bound` would have `type QueryId = + /// Bound`. + /// + /// If `HAS_STATIC_QUERY_ID` is `false`, you can put any type here + /// (typically `()`). + type QueryId: Any; + + /// Can the SQL generated by `Self` be uniquely identified by its type? + /// + /// Typically this question can be answered by looking at whether + /// `unsafe_to_cache_prepared` is called in your implementation of + /// `QueryFragment::walk_ast`. In Diesel itself, the only type which has + /// `false` here, but is potentially safe to store in the prepared statement + /// cache is a boxed query. + const HAS_STATIC_QUERY_ID: bool = true; + + /// Returns the type id of `Self::QueryId` if `Self::HAS_STATIC_QUERY_ID`. + /// Returns `None` otherwise. + /// + /// You should never need to override this method. + fn query_id() -> Option { + if Self::HAS_STATIC_QUERY_ID { + Some(TypeId::of::()) + } else { + None + } + } +} + +#[doc(inline)] +pub use diesel_derives::QueryId; + +impl QueryId for () { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = true; +} + +impl QueryId for Box { + type QueryId = T::QueryId; + + const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID; +} + +impl<'a, T: QueryId + ?Sized> QueryId for &'a T { + type QueryId = T::QueryId; + + const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID; +} + +impl QueryId for dyn QueryFragment { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +#[cfg(test)] +#[allow(unused_parens)] // FIXME: Remove this attribute once false positive is resolved. +mod tests { + use std::any::TypeId; + + use super::QueryId; + use crate::prelude::*; + + table! { + users { + id -> Integer, + name -> VarChar, + } + } + + fn query_id(_: T) -> Option { + T::query_id() + } + + #[test] + fn queries_with_no_dynamic_elements_have_a_static_id() { + use self::users::dsl::*; + assert!(query_id(users).is_some()); + assert!(query_id(users.select(name)).is_some()); + assert!(query_id(users.filter(name.eq("Sean"))).is_some()); + } + + #[test] + fn queries_with_different_types_have_different_ids() { + let id1 = query_id(users::table.select(users::name)); + let id2 = query_id(users::table.select(users::id)); + assert_ne!(id1, id2); + } + + #[test] + fn bind_params_use_only_sql_type_for_query_id() { + use self::users::dsl::*; + let id1 = query_id(users.filter(name.eq("Sean"))); + let id2 = query_id(users.filter(name.eq("Tess".to_string()))); + + assert_eq!(id1, id2); + } + + #[test] + #[cfg(features = "postgres")] + fn boxed_queries_do_not_have_static_query_id() { + use pg::Pg; + assert!(query_id(users::table.into_boxed::()).is_none()); + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/returning_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/returning_clause.rs new file mode 100644 index 000000000..a76d3d4e7 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/returning_clause.rs @@ -0,0 +1,8 @@ +use crate::backend::SupportsReturningClause; + +simple_clause!( + NoReturningClause, + ReturningClause, + " RETURNING ", + backend_bounds = SupportsReturningClause +); diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/select_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/select_clause.rs new file mode 100644 index 000000000..fea6ed318 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/select_clause.rs @@ -0,0 +1,110 @@ +use crate::backend::Backend; +use crate::expression::{Expression, SelectableExpression}; +use crate::query_builder::*; +use crate::query_source::QuerySource; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct DefaultSelectClause; +#[derive(Debug, Clone, Copy, QueryId)] +pub struct SelectClause(pub T); + +/// Specialised variant of `Expression` for select clause types +/// +/// The difference to the normal `Expression` trait is the query source (`QS`) +/// generic type parameter. This allows to access the query source in generic code. +pub trait SelectClauseExpression { + /// The expression represented by the given select clause + type Selection: SelectableExpression; + /// SQL type of the select clause + type SelectClauseSqlType; +} + +impl SelectClauseExpression for SelectClause +where + T: SelectableExpression, +{ + type Selection = T; + type SelectClauseSqlType = T::SqlType; +} + +impl SelectClauseExpression for DefaultSelectClause +where + QS: QuerySource, +{ + type Selection = QS::DefaultSelection; + type SelectClauseSqlType = ::SqlType; +} + +/// Specialised variant of `QueryFragment` for select clause types +/// +/// The difference to the normal `QueryFragment` trait is the query source (`QS`) +/// generic type parameter. +pub trait SelectClauseQueryFragment { + /// Walk over this `SelectClauseQueryFragment` for all passes. + /// + /// This method is where the actual behavior of an select clause is implemented. + /// This method will contain the behavior required for all possible AST + /// passes. See [`AstPass`] for more details. + /// + /// [`AstPass`]: struct.AstPass.html + fn walk_ast(&self, source: &QS, pass: AstPass) -> QueryResult<()>; +} + +impl SelectClauseQueryFragment for SelectClause +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, _: &QS, pass: AstPass) -> QueryResult<()> { + self.0.walk_ast(pass) + } +} + +impl SelectClauseQueryFragment for DefaultSelectClause +where + DB: Backend, + QS: QuerySource, + QS::DefaultSelection: QueryFragment, +{ + fn walk_ast(&self, source: &QS, pass: AstPass) -> QueryResult<()> { + source.default_selection().walk_ast(pass) + } +} + +/// An internal helper trait to convert different select clauses +/// into their boxed counter part. +/// +/// You normally don't need this trait, at least as long as you +/// don't implement your own select clause representation +pub trait IntoBoxedSelectClause<'a, DB, QS> { + /// The sql type of the select clause + type SqlType; + + /// Convert the select clause into a the boxed representation + fn into_boxed(self, source: &QS) -> Box + Send + 'a>; +} + +impl<'a, DB, T, QS> IntoBoxedSelectClause<'a, DB, QS> for SelectClause +where + T: QueryFragment + SelectableExpression + Send + 'a, + DB: Backend, +{ + type SqlType = T::SqlType; + + fn into_boxed(self, _source: &QS) -> Box + Send + 'a> { + Box::new(self.0) + } +} + +impl<'a, DB, QS> IntoBoxedSelectClause<'a, DB, QS> for DefaultSelectClause +where + QS: QuerySource, + QS::DefaultSelection: QueryFragment + Send + 'a, + DB: Backend, +{ + type SqlType = ::SqlType; + + fn into_boxed(self, source: &QS) -> Box + Send + 'a> { + Box::new(source.default_selection()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/select_statement/boxed.rs b/collector/benchmarks/diesel/diesel/src/query_builder/select_statement/boxed.rs new file mode 100644 index 000000000..db02a2538 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/select_statement/boxed.rs @@ -0,0 +1,392 @@ +use std::marker::PhantomData; + +use crate::backend::Backend; +use crate::dsl::AsExprOf; +use crate::expression::subselect::ValidSubselect; +use crate::expression::*; +use crate::insertable::Insertable; +use crate::query_builder::distinct_clause::DistinctClause; +use crate::query_builder::group_by_clause::ValidGroupByClause; +use crate::query_builder::insert_statement::InsertFromSelect; +use crate::query_builder::limit_clause::LimitClause; +use crate::query_builder::limit_offset_clause::BoxedLimitOffsetClause; +use crate::query_builder::offset_clause::OffsetClause; +use crate::query_builder::order_clause::OrderClause; +use crate::query_builder::where_clause::*; +use crate::query_builder::*; +use crate::query_dsl::methods::*; +use crate::query_dsl::*; +use crate::query_source::joins::*; +use crate::query_source::{QuerySource, Table}; +use crate::result::QueryResult; +use crate::sql_types::{BigInt, BoolOrNullableBool, IntoNullable}; + +#[allow(missing_debug_implementations)] +pub struct BoxedSelectStatement<'a, ST, QS, DB, GB = ()> { + select: Box + Send + 'a>, + from: QS, + distinct: Box + Send + 'a>, + where_clause: BoxedWhereClause<'a, DB>, + order: Option + Send + 'a>>, + limit_offset: BoxedLimitOffsetClause<'a, DB>, + group_by: Box + Send + 'a>, + _marker: PhantomData<(ST, GB)>, +} + +impl<'a, ST, QS, DB, GB> BoxedSelectStatement<'a, ST, QS, DB, GB> { + #[allow(clippy::too_many_arguments)] + pub fn new( + select: S, + from: QS, + distinct: Box + Send + 'a>, + where_clause: BoxedWhereClause<'a, DB>, + order: Option + Send + 'a>>, + limit_offset: BoxedLimitOffsetClause<'a, DB>, + group_by: G, + ) -> Self + where + DB: Backend, + G: ValidGroupByClause + QueryFragment + Send + 'a, + S: IntoBoxedSelectClause<'a, DB, QS> + SelectClauseExpression, + S::Selection: ValidGrouping, + { + BoxedSelectStatement { + select: select.into_boxed(&from), + from, + distinct, + where_clause, + order, + limit_offset, + group_by: Box::new(group_by), + _marker: PhantomData, + } + } +} + +impl<'a, ST, QS, DB, GB> BoxedSelectStatement<'a, ST, QS, DB, GB> { + pub(crate) fn build_query( + &self, + mut out: AstPass, + where_clause_handler: impl Fn(&BoxedWhereClause<'a, DB>, AstPass) -> QueryResult<()>, + ) -> QueryResult<()> + where + DB: Backend, + QS: QuerySource, + QS::FromClause: QueryFragment, + BoxedLimitOffsetClause<'a, DB>: QueryFragment, + { + out.push_sql("SELECT "); + self.distinct.walk_ast(out.reborrow())?; + self.select.walk_ast(out.reborrow())?; + out.push_sql(" FROM "); + self.from.from_clause().walk_ast(out.reborrow())?; + where_clause_handler(&self.where_clause, out.reborrow())?; + self.group_by.walk_ast(out.reborrow())?; + + if let Some(ref order) = self.order { + out.push_sql(" ORDER BY "); + order.walk_ast(out.reborrow())?; + } + self.limit_offset.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl<'a, ST, QS, DB, GB> Query for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + DB: Backend, +{ + type SqlType = ST; +} + +impl<'a, ST, QS, DB, GB> SelectQuery for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + DB: Backend, +{ + type SqlType = ST; +} + +impl<'a, ST, QS, QS2, DB, GB> ValidSubselect for BoxedSelectStatement<'a, ST, QS, DB, GB> where + Self: Query +{ +} + +impl<'a, ST, QS, DB, GB> QueryFragment for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + DB: Backend, + QS: QuerySource, + QS::FromClause: QueryFragment, + BoxedLimitOffsetClause<'a, DB>: QueryFragment, +{ + fn walk_ast(&self, out: AstPass) -> QueryResult<()> { + self.build_query(out, |where_clause, out| where_clause.walk_ast(out)) + } +} + +impl<'a, ST, DB, GB> QueryFragment for BoxedSelectStatement<'a, ST, (), DB, GB> +where + DB: Backend, + BoxedLimitOffsetClause<'a, DB>: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("SELECT "); + self.distinct.walk_ast(out.reborrow())?; + self.select.walk_ast(out.reborrow())?; + self.where_clause.walk_ast(out.reborrow())?; + self.group_by.walk_ast(out.reborrow())?; + self.order.walk_ast(out.reborrow())?; + self.limit_offset.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl<'a, ST, QS, DB, GB> QueryId for BoxedSelectStatement<'a, ST, QS, DB, GB> { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl<'a, ST, QS, DB, Rhs, Kind, On, GB> InternalJoinDsl + for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + BoxedSelectStatement<'a, ST, JoinOn, On>, DB, GB>: AsQuery, +{ + type Output = BoxedSelectStatement<'a, ST, JoinOn, On>, DB, GB>; + + fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output { + BoxedSelectStatement { + select: self.select, + from: Join::new(self.from, rhs, kind).on(on), + distinct: self.distinct, + where_clause: self.where_clause, + order: self.order, + limit_offset: self.limit_offset, + group_by: self.group_by, + _marker: PhantomData, + } + } +} + +impl<'a, ST, QS, DB, GB> DistinctDsl for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + DB: Backend, + DistinctClause: QueryFragment, +{ + type Output = Self; + + fn distinct(mut self) -> Self::Output { + self.distinct = Box::new(DistinctClause); + self + } +} + +impl<'a, ST, QS, DB, Selection, GB> SelectDsl + for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + DB: Backend, + Selection: SelectableExpression + QueryFragment + ValidGrouping + Send + 'a, +{ + type Output = BoxedSelectStatement<'a, Selection::SqlType, QS, DB, GB>; + + fn select(self, selection: Selection) -> Self::Output { + BoxedSelectStatement { + select: Box::new(selection), + from: self.from, + distinct: self.distinct, + where_clause: self.where_clause, + order: self.order, + limit_offset: self.limit_offset, + group_by: self.group_by, + _marker: PhantomData, + } + } +} + +impl<'a, ST, QS, DB, Predicate, GB> FilterDsl + for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + BoxedWhereClause<'a, DB>: WhereAnd>, + Predicate: AppearsOnTable + NonAggregate, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = Self; + + fn filter(mut self, predicate: Predicate) -> Self::Output { + self.where_clause = self.where_clause.and(predicate); + self + } +} + +impl<'a, ST, QS, DB, Predicate, GB> OrFilterDsl + for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + BoxedWhereClause<'a, DB>: WhereOr>, + Predicate: AppearsOnTable + NonAggregate, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = Self; + + fn or_filter(mut self, predicate: Predicate) -> Self::Output { + self.where_clause = self.where_clause.or(predicate); + self + } +} + +impl<'a, ST, QS, DB, GB> LimitDsl for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + DB: Backend, + LimitClause>: QueryFragment, +{ + type Output = Self; + + fn limit(mut self, limit: i64) -> Self::Output { + self.limit_offset.limit = Some(Box::new(LimitClause(limit.into_sql::()))); + self + } +} + +impl<'a, ST, QS, DB, GB> OffsetDsl for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + DB: Backend, + OffsetClause>: QueryFragment, +{ + type Output = Self; + + fn offset(mut self, offset: i64) -> Self::Output { + self.limit_offset.offset = Some(Box::new(OffsetClause(offset.into_sql::()))); + self + } +} + +impl<'a, ST, QS, DB, Order, GB> OrderDsl for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + DB: Backend, + Order: QueryFragment + AppearsOnTable + Send + 'a, +{ + type Output = Self; + + fn order(mut self, order: Order) -> Self::Output { + self.order = OrderClause(order).into(); + self + } +} + +impl<'a, ST, QS, DB, Order, GB> ThenOrderDsl for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + DB: Backend + 'a, + Order: QueryFragment + AppearsOnTable + Send + 'a, +{ + type Output = Self; + + fn then_order_by(mut self, order: Order) -> Self::Output { + self.order = match self.order { + Some(old) => Some(Box::new((old, order))), + None => Some(Box::new(order)), + }; + self + } +} + +impl<'a, ST, QS, DB, Rhs> JoinTo for BoxedSelectStatement<'a, ST, QS, DB, ()> +where + QS: JoinTo, +{ + type FromClause = QS::FromClause; + type OnClause = QS::OnClause; + + fn join_target(rhs: Rhs) -> (Self::FromClause, Self::OnClause) { + QS::join_target(rhs) + } +} + +impl<'a, ST, QS, DB, GB> QueryDsl for BoxedSelectStatement<'a, ST, QS, DB, GB> {} + +impl<'a, ST, QS, DB, Conn, GB> RunQueryDsl for BoxedSelectStatement<'a, ST, QS, DB, GB> {} + +impl<'a, ST, QS, DB, T, GB> Insertable for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + T: Table, + Self: Query, + >::IsAggregate: + MixedAggregates, +{ + type Values = InsertFromSelect; + + fn values(self) -> Self::Values { + InsertFromSelect::new(self) + } +} + +impl<'a, 'b, ST, QS, DB, T, GB> Insertable for &'b BoxedSelectStatement<'a, ST, QS, DB, GB> +where + T: Table, + Self: Query, + >::IsAggregate: + MixedAggregates, +{ + type Values = InsertFromSelect; + + fn values(self) -> Self::Values { + InsertFromSelect::new(self) + } +} + +impl<'a, ST, QS, DB, GB> SelectNullableDsl for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + ST: IntoNullable, +{ + type Output = BoxedSelectStatement<'a, ST::Nullable, QS, DB>; + + fn nullable(self) -> Self::Output { + BoxedSelectStatement { + select: self.select, + from: self.from, + distinct: self.distinct, + where_clause: self.where_clause, + order: self.order, + limit_offset: self.limit_offset, + group_by: self.group_by, + _marker: PhantomData, + } + } +} + +#[cfg(test)] +mod tests { + use crate::prelude::*; + + table! { + users { + id -> Integer, + } + } + + fn assert_send(_: T) + where + T: Send, + { + } + + macro_rules! assert_boxed_query_send { + ($backend:ty) => {{ + assert_send(users::table.into_boxed::<$backend>()); + assert_send( + users::table + .filter(users::id.eq(10)) + .into_boxed::<$backend>(), + ); + };}; + } + + #[test] + fn boxed_is_send() { + #[cfg(feature = "postgres")] + assert_boxed_query_send!(crate::pg::Pg); + + #[cfg(feature = "sqlite")] + assert_boxed_query_send!(crate::sqlite::Sqlite); + + #[cfg(feature = "mysql")] + assert_boxed_query_send!(crate::mysql::Mysql); + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/select_statement/dsl_impls.rs b/collector/benchmarks/diesel/diesel/src/query_builder/select_statement/dsl_impls.rs new file mode 100644 index 000000000..dcdad056c --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/select_statement/dsl_impls.rs @@ -0,0 +1,487 @@ +use super::BoxedSelectStatement; +use crate::associations::HasTable; +use crate::backend::Backend; +use crate::dsl::AsExprOf; +use crate::expression::nullable::Nullable; +use crate::expression::*; +use crate::insertable::Insertable; +use crate::query_builder::distinct_clause::*; +use crate::query_builder::group_by_clause::*; +use crate::query_builder::insert_statement::InsertFromSelect; +use crate::query_builder::limit_clause::*; +use crate::query_builder::limit_offset_clause::{BoxedLimitOffsetClause, LimitOffsetClause}; +use crate::query_builder::locking_clause::*; +use crate::query_builder::offset_clause::*; +use crate::query_builder::order_clause::*; +use crate::query_builder::select_clause::*; +use crate::query_builder::update_statement::*; +use crate::query_builder::where_clause::*; +use crate::query_builder::{ + AsQuery, IntoBoxedClause, Query, QueryFragment, SelectQuery, SelectStatement, +}; +use crate::query_dsl::boxed_dsl::BoxedDsl; +use crate::query_dsl::methods::*; +use crate::query_dsl::*; +use crate::query_source::joins::{Join, JoinOn, JoinTo}; +use crate::query_source::QuerySource; +use crate::sql_types::{BigInt, BoolOrNullableBool}; + +impl InternalJoinDsl + for SelectStatement +where + SelectStatement, On>, S, D, W, O, LOf, G, LC>: AsQuery, +{ + type Output = SelectStatement, On>, S, D, W, O, LOf, G, LC>; + + fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output { + SelectStatement::new( + self.select, + Join::new(self.from, rhs, kind).on(on), + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.locking, + ) + } +} + +impl SelectDsl + for SelectStatement +where + G: ValidGroupByClause, + Selection: SelectableExpression + ValidGrouping, + SelectStatement, D, W, O, LOf, G, LC>: SelectQuery, +{ + type Output = SelectStatement, D, W, O, LOf, G, LC>; + + fn select(self, selection: Selection) -> Self::Output { + SelectStatement::new( + SelectClause(selection), + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.locking, + ) + } +} + +impl DistinctDsl for SelectStatement +where + Self: SelectQuery, + SelectStatement: SelectQuery, +{ + type Output = SelectStatement; + + fn distinct(self) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + DistinctClause, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.locking, + ) + } +} + +impl FilterDsl + for SelectStatement +where + Predicate: Expression + NonAggregate, + Predicate::SqlType: BoolOrNullableBool, + W: WhereAnd, +{ + type Output = SelectStatement; + + fn filter(self, predicate: Predicate) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause.and(predicate), + self.order, + self.limit_offset, + self.group_by, + self.locking, + ) + } +} + +impl OrFilterDsl + for SelectStatement +where + Predicate: Expression + NonAggregate, + Predicate::SqlType: BoolOrNullableBool, + W: WhereOr, +{ + type Output = SelectStatement; + + fn or_filter(self, predicate: Predicate) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause.or(predicate), + self.order, + self.limit_offset, + self.group_by, + self.locking, + ) + } +} + +use crate::dsl::Filter; +use crate::expression_methods::EqAll; +use crate::query_source::Table; + +impl FindDsl for SelectStatement +where + F: Table, + F::PrimaryKey: EqAll, + Self: FilterDsl<>::Output>, +{ + type Output = Filter>::Output>; + + fn find(self, id: PK) -> Self::Output { + let primary_key = self.from.primary_key(); + FilterDsl::filter(self, primary_key.eq_all(id)) + } +} + +impl OrderDsl + for SelectStatement +where + Expr: AppearsOnTable, + Self: SelectQuery, + SelectStatement, LOf, G, LC>: SelectQuery, +{ + type Output = SelectStatement, LOf, G, LC>; + + fn order(self, expr: Expr) -> Self::Output { + let order = OrderClause(expr); + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + order, + self.limit_offset, + self.group_by, + self.locking, + ) + } +} + +impl ThenOrderDsl + for SelectStatement, LOf, G, LC> +where + Expr: AppearsOnTable, +{ + type Output = SelectStatement, LOf, G, LC>; + + fn then_order_by(self, expr: Expr) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + OrderClause((self.order.0, expr)), + self.limit_offset, + self.group_by, + self.locking, + ) + } +} + +impl ThenOrderDsl + for SelectStatement +where + Expr: Expression, + Self: OrderDsl, +{ + type Output = crate::dsl::Order; + + fn then_order_by(self, expr: Expr) -> Self::Output { + self.order_by(expr) + } +} + +#[doc(hidden)] +pub type Limit = AsExprOf; + +impl LimitDsl + for SelectStatement, G, LC> +where + Self: SelectQuery, + SelectStatement, Of>, G, LC>: + SelectQuery, +{ + type Output = SelectStatement, Of>, G, LC>; + + fn limit(self, limit: i64) -> Self::Output { + let limit_clause = LimitClause(limit.into_sql::()); + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + self.order, + LimitOffsetClause { + limit_clause, + offset_clause: self.limit_offset.offset_clause, + }, + self.group_by, + self.locking, + ) + } +} + +#[doc(hidden)] +pub type Offset = Limit; + +impl OffsetDsl + for SelectStatement, G, LC> +where + Self: SelectQuery, + SelectStatement>, G, LC>: + SelectQuery, +{ + type Output = SelectStatement>, G, LC>; + + fn offset(self, offset: i64) -> Self::Output { + let offset_clause = OffsetClause(offset.into_sql::()); + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + self.order, + LimitOffsetClause { + limit_clause: self.limit_offset.limit_clause, + offset_clause, + }, + self.group_by, + self.locking, + ) + } +} + +impl GroupByDsl for SelectStatement +where + SelectStatement>: SelectQuery, + Expr: Expression, +{ + type Output = SelectStatement>; + + fn group_by(self, expr: Expr) -> Self::Output { + let group_by = GroupByClause(expr); + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + group_by, + self.locking, + ) + } +} + +impl LockingDsl + for SelectStatement +{ + type Output = SelectStatement< + F, + S, + NoDistinctClause, + W, + O, + LOf, + NoGroupByClause, + LockingClause, + >; + + fn with_lock(self, lock: Lock) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + LockingClause::new(lock, NoModifier), + ) + } +} + +impl ModifyLockDsl + for SelectStatement> +{ + type Output = SelectStatement>; + + fn modify_lock(self, modifier: Modifier) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + LockingClause::new(self.locking.lock_mode, modifier), + ) + } +} + +impl<'a, F, S, D, W, O, LOf, G, DB> BoxedDsl<'a, DB> for SelectStatement +where + Self: AsQuery, + DB: Backend, + S: IntoBoxedSelectClause<'a, DB, F> + SelectClauseExpression, + S::Selection: ValidGrouping, + D: QueryFragment + Send + 'a, + W: Into>, + O: Into + Send + 'a>>>, + LOf: IntoBoxedClause<'a, DB, BoxedClause = BoxedLimitOffsetClause<'a, DB>>, + G: ValidGroupByClause + QueryFragment + Send + 'a, +{ + type Output = BoxedSelectStatement<'a, S::SqlType, F, DB, G::Expressions>; + + fn internal_into_boxed(self) -> Self::Output { + BoxedSelectStatement::new( + self.select, + self.from, + Box::new(self.distinct), + self.where_clause.into(), + self.order.into(), + self.limit_offset.into_boxed(), + self.group_by, + ) + } +} + +impl HasTable for SelectStatement +where + F: HasTable, +{ + type Table = F::Table; + + fn table() -> Self::Table { + F::table() + } +} + +impl IntoUpdateTarget for SelectStatement +where + SelectStatement: HasTable, + W: ValidWhereClause, +{ + type WhereClause = W; + + fn into_update_target(self) -> UpdateTarget { + UpdateTarget { + table: Self::table(), + where_clause: self.where_clause, + } + } +} + +// FIXME: Should we disable joining when `.group_by` has been called? Are there +// any other query methods where a join no longer has the same semantics as +// joining on just the table? +impl JoinTo for SelectStatement +where + F: JoinTo, +{ + type FromClause = F::FromClause; + type OnClause = F::OnClause; + + fn join_target(rhs: Rhs) -> (Self::FromClause, Self::OnClause) { + F::join_target(rhs) + } +} + +impl QueryDsl for SelectStatement {} + +impl RunQueryDsl + for SelectStatement +{ +} + +impl Insertable for SelectStatement +where + Tab: Table, + Self: Query, + >::IsAggregate: + MixedAggregates, +{ + type Values = InsertFromSelect; + + fn values(self) -> Self::Values { + InsertFromSelect::new(self) + } +} + +impl<'a, F, S, D, W, O, LOf, G, LC, Tab> Insertable + for &'a SelectStatement +where + Tab: Table, + Self: Query, + >::IsAggregate: + MixedAggregates, +{ + type Values = InsertFromSelect; + + fn values(self) -> Self::Values { + InsertFromSelect::new(self) + } +} + +impl<'a, F, S, D, W, O, LOf, G> SelectNullableDsl + for SelectStatement, D, W, O, LOf, G> +{ + type Output = SelectStatement>, D, W, O, LOf, G>; + + fn nullable(self) -> Self::Output { + SelectStatement::new( + SelectClause(Nullable::new(self.select.0)), + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.locking, + ) + } +} + +impl<'a, F, D, W, O, LOf, G> SelectNullableDsl + for SelectStatement +where + F: QuerySource, +{ + type Output = SelectStatement>, D, W, O, LOf, G>; + + fn nullable(self) -> Self::Output { + SelectStatement::new( + SelectClause(Nullable::new(self.from.default_selection())), + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.locking, + ) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/select_statement/mod.rs b/collector/benchmarks/diesel/diesel/src/query_builder/select_statement/mod.rs new file mode 100644 index 000000000..0998377b9 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/select_statement/mod.rs @@ -0,0 +1,215 @@ +//! Within this module, types commonly use the following abbreviations: +//! +//! F: From Clause +//! S: Select Clause +//! D: Distinct Clause +//! W: Where Clause +//! O: Order By Clause +//! L: Limit Clause +//! Of: Offset Clause +//! G: Group By Clause +//! LC: For Update Clause +#![allow(missing_docs)] // The missing_docs lint triggers even though this is hidden + +mod boxed; +mod dsl_impls; + +pub use self::boxed::BoxedSelectStatement; + +use super::distinct_clause::NoDistinctClause; +use super::group_by_clause::*; +use super::limit_clause::NoLimitClause; +use super::locking_clause::NoLockingClause; +use super::offset_clause::NoOffsetClause; +use super::order_clause::NoOrderClause; +use super::select_clause::*; +use super::where_clause::*; +use super::{AstPass, Query, QueryFragment}; +use crate::backend::Backend; +use crate::expression::subselect::ValidSubselect; +use crate::expression::*; +use crate::query_builder::limit_offset_clause::LimitOffsetClause; +use crate::query_builder::{QueryId, SelectQuery}; +use crate::query_source::joins::{AppendSelection, Inner, Join}; +use crate::query_source::*; +use crate::result::QueryResult; + +#[derive(Debug, Clone, Copy, QueryId)] +#[doc(hidden)] +#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."] +pub struct SelectStatement< + From, + Select = DefaultSelectClause, + Distinct = NoDistinctClause, + Where = NoWhereClause, + Order = NoOrderClause, + LimitOffset = LimitOffsetClause, + GroupBy = NoGroupByClause, + Locking = NoLockingClause, +> { + pub(crate) select: Select, + pub(crate) from: From, + pub(crate) distinct: Distinct, + pub(crate) where_clause: Where, + pub(crate) order: Order, + pub(crate) limit_offset: LimitOffset, + pub(crate) group_by: GroupBy, + pub(crate) locking: Locking, +} + +impl SelectStatement { + #[allow(clippy::too_many_arguments)] + pub fn new( + select: S, + from: F, + distinct: D, + where_clause: W, + order: O, + limit_offset: LOf, + group_by: G, + locking: LC, + ) -> Self { + SelectStatement { + select, + from, + distinct, + where_clause, + order, + limit_offset, + group_by, + locking, + } + } +} + +impl SelectStatement { + pub fn simple(from: F) -> Self { + SelectStatement::new( + DefaultSelectClause, + from, + NoDistinctClause, + NoWhereClause, + NoOrderClause, + LimitOffsetClause { + limit_clause: NoLimitClause, + offset_clause: NoOffsetClause, + }, + NoGroupByClause, + NoLockingClause, + ) + } +} + +impl Query for SelectStatement +where + G: ValidGroupByClause, + S: SelectClauseExpression, + S::Selection: ValidGrouping, + W: ValidWhereClause, +{ + type SqlType = S::SelectClauseSqlType; +} + +impl SelectQuery for SelectStatement +where + S: SelectClauseExpression, +{ + type SqlType = S::SelectClauseSqlType; +} + +impl QueryFragment for SelectStatement +where + DB: Backend, + S: SelectClauseQueryFragment, + F: QuerySource, + F::FromClause: QueryFragment, + D: QueryFragment, + W: QueryFragment, + O: QueryFragment, + LOf: QueryFragment, + G: QueryFragment, + LC: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("SELECT "); + self.distinct.walk_ast(out.reborrow())?; + self.select.walk_ast(&self.from, out.reborrow())?; + out.push_sql(" FROM "); + self.from.from_clause().walk_ast(out.reborrow())?; + self.where_clause.walk_ast(out.reborrow())?; + self.group_by.walk_ast(out.reborrow())?; + self.order.walk_ast(out.reborrow())?; + self.limit_offset.walk_ast(out.reborrow())?; + self.locking.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl QueryFragment for SelectStatement<(), S, D, W, O, LOf, G, LC> +where + DB: Backend, + S: SelectClauseQueryFragment<(), DB>, + D: QueryFragment, + W: QueryFragment, + O: QueryFragment, + LOf: QueryFragment, + G: QueryFragment, + LC: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("SELECT "); + self.distinct.walk_ast(out.reborrow())?; + self.select.walk_ast(&(), out.reborrow())?; + self.where_clause.walk_ast(out.reborrow())?; + self.group_by.walk_ast(out.reborrow())?; + self.order.walk_ast(out.reborrow())?; + self.limit_offset.walk_ast(out.reborrow())?; + self.locking.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl ValidSubselect + for SelectStatement +where + Self: SelectQuery, + W: ValidWhereClause>, +{ +} + +/// Allow `SelectStatement` to act as if it were `From` as long as +/// no other query methods have been called on it +impl AppearsInFromClause for SelectStatement +where + From: AppearsInFromClause, +{ + type Count = From::Count; +} + +impl QuerySource for SelectStatement +where + From: QuerySource, + From::DefaultSelection: SelectableExpression, +{ + type FromClause = From::FromClause; + type DefaultSelection = From::DefaultSelection; + + fn from_clause(&self) -> Self::FromClause { + self.from.from_clause() + } + + fn default_selection(&self) -> Self::DefaultSelection { + self.from.default_selection() + } +} + +impl AppendSelection for SelectStatement +where + From: AppendSelection, +{ + type Output = From::Output; + + fn append_selection(&self, selection: Selection) -> Self::Output { + self.from.append_selection(selection) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/sql_query.rs b/collector/benchmarks/diesel/diesel/src/query_builder/sql_query.rs new file mode 100644 index 000000000..f838cc3c5 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/sql_query.rs @@ -0,0 +1,256 @@ +use std::marker::PhantomData; + +use super::Query; +use crate::backend::Backend; +use crate::connection::Connection; +use crate::query_builder::{AstPass, QueryFragment, QueryId}; +use crate::query_dsl::RunQueryDsl; +use crate::result::QueryResult; +use crate::serialize::ToSql; +use crate::sql_types::{HasSqlType, Untyped}; + +#[derive(Debug, Clone)] +#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."] +/// The return value of `sql_query`. +/// +/// Unlike most queries in Diesel, `SqlQuery` loads its data by column name, +/// rather than by index. This means that you cannot deserialize this query into +/// a tuple, and any structs used must implement `QueryableByName`. +/// +/// See [`sql_query`](../fn.sql_query.html) for examples. +pub struct SqlQuery { + inner: Inner, + query: String, +} + +impl SqlQuery { + pub(crate) fn new(inner: Inner, query: String) -> Self { + SqlQuery { inner, query } + } + + /// Bind a value for use with this SQL query. The given query should have + /// placeholders that vary based on the database type, + /// like [SQLite Parameter](https://sqlite.org/lang_expr.html#varparam) syntax, + /// [PostgreSQL PREPARE syntax](https://www.postgresql.org/docs/current/sql-prepare.html), + /// or [MySQL bind syntax](https://dev.mysql.com/doc/refman/8.0/en/mysql-stmt-bind-param.html). + /// + /// # Safety + /// + /// This function should be used with care, as Diesel cannot validate that + /// the value is of the right type nor can it validate that you have passed + /// the correct number of parameters. + /// + /// # Example + /// + /// ``` + /// # include!("../doctest_setup.rs"); + /// # + /// # use schema::users; + /// # + /// # #[derive(QueryableByName, Debug, PartialEq)] + /// # #[table_name="users"] + /// # struct User { + /// # id: i32, + /// # name: String, + /// # } + /// # + /// # fn main() { + /// # use diesel::sql_query; + /// # use diesel::sql_types::{Integer, Text}; + /// # + /// # let connection = establish_connection(); + /// # diesel::insert_into(users::table) + /// # .values(users::name.eq("Jim")) + /// # .execute(&connection).unwrap(); + /// # #[cfg(feature = "postgres")] + /// # let users = sql_query("SELECT * FROM users WHERE id > $1 AND name != $2"); + /// # #[cfg(not(feature = "postgres"))] + /// let users = sql_query("SELECT * FROM users WHERE id > ? AND name <> ?") + /// # ; + /// # let users = users + /// .bind::(1) + /// .bind::("Tess") + /// .get_results(&connection); + /// let expected_users = vec![ + /// User { id: 3, name: "Jim".into() }, + /// ]; + /// assert_eq!(Ok(expected_users), users); + /// # } + /// ``` + pub fn bind(self, value: Value) -> UncheckedBind { + UncheckedBind::new(self, value) + } + + /// Internally boxes future calls on `bind` and `sql` so that they don't + /// change the type. + /// + /// This allows doing things you otherwise couldn't do, e.g. `bind`ing in a + /// loop. + pub fn into_boxed<'f, DB: Backend>(self) -> BoxedSqlQuery<'f, DB, Self> { + BoxedSqlQuery::new(self) + } + + /// Appends a piece of SQL code at the end. + pub fn sql>(mut self, sql: T) -> Self { + self.query += sql.as_ref(); + self + } +} + +impl QueryFragment for SqlQuery +where + DB: Backend, + Inner: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + self.inner.walk_ast(out.reborrow())?; + out.push_sql(&self.query); + Ok(()) + } +} + +impl QueryId for SqlQuery { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl Query for SqlQuery { + type SqlType = Untyped; +} + +impl RunQueryDsl for SqlQuery {} + +#[derive(Debug, Clone, Copy)] +#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."] +pub struct UncheckedBind { + query: Query, + value: Value, + _marker: PhantomData, +} + +impl UncheckedBind { + pub fn new(query: Query, value: Value) -> Self { + UncheckedBind { + query, + value, + _marker: PhantomData, + } + } + + pub fn bind(self, value: Value2) -> UncheckedBind { + UncheckedBind::new(self, value) + } + + pub fn into_boxed<'f, DB: Backend>(self) -> BoxedSqlQuery<'f, DB, Self> { + BoxedSqlQuery::new(self) + } + + pub fn sql>(self, sql: T) -> SqlQuery { + SqlQuery::new(self, sql.into()) + } +} + +impl QueryId for UncheckedBind +where + Query: QueryId, + ST: QueryId, +{ + type QueryId = UncheckedBind; + + const HAS_STATIC_QUERY_ID: bool = Query::HAS_STATIC_QUERY_ID && ST::HAS_STATIC_QUERY_ID; +} + +impl QueryFragment for UncheckedBind +where + DB: Backend + HasSqlType, + Query: QueryFragment, + Value: ToSql, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.query.walk_ast(out.reborrow())?; + out.push_bind_param_value_only(&self.value)?; + Ok(()) + } +} + +impl Query for UncheckedBind { + type SqlType = Untyped; +} + +impl RunQueryDsl for UncheckedBind {} + +#[must_use = "Queries are only executed when calling `load`, `get_result`, or similar."] +/// See [`SqlQuery::into_boxed`]. +/// +/// [`SqlQuery::into_boxed`]: ./struct.SqlQuery.html#method.into_boxed +#[allow(missing_debug_implementations)] +pub struct BoxedSqlQuery<'f, DB: Backend, Query> { + query: Query, + sql: String, + binds: Vec) -> QueryResult<()> + 'f>>, +} + +impl<'f, DB: Backend, Query> BoxedSqlQuery<'f, DB, Query> { + pub(crate) fn new(query: Query) -> Self { + BoxedSqlQuery { + query, + sql: "".to_string(), + binds: vec![], + } + } + + /// See [`SqlQuery::bind`]. + /// + /// [`SqlQuery::bind`]: ./struct.SqlQuery.html#method.bind + pub fn bind(mut self, b: Value) -> Self + where + DB: HasSqlType, + Value: ToSql + 'f, + { + self.binds + .push(Box::new(move |mut out| out.push_bind_param_value_only(&b))); + self + } + + /// See [`SqlQuery::sql`]. + /// + /// [`SqlQuery::sql`]: ./struct.SqlQuery.html#method.sql + pub fn sql>(mut self, sql: T) -> Self { + self.sql += sql.as_ref(); + self + } +} + +impl QueryFragment for BoxedSqlQuery<'_, DB, Query> +where + DB: Backend, + Query: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + self.query.walk_ast(out.reborrow())?; + out.push_sql(&self.sql); + + for b in &self.binds { + b(out.reborrow())?; + } + Ok(()) + } +} + +impl QueryId for BoxedSqlQuery<'_, DB, Query> { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl Query for BoxedSqlQuery<'_, DB, Q> +where + DB: Backend, +{ + type SqlType = Untyped; +} + +impl RunQueryDsl for BoxedSqlQuery<'_, Conn::Backend, Query> {} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/update_statement/changeset.rs b/collector/benchmarks/diesel/diesel/src/query_builder/update_statement/changeset.rs new file mode 100644 index 000000000..75b7d3234 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/update_statement/changeset.rs @@ -0,0 +1,82 @@ +use crate::backend::Backend; +use crate::expression::grouped::Grouped; +use crate::expression::operators::Eq; +use crate::expression::AppearsOnTable; +use crate::query_builder::*; +use crate::query_source::{Column, QuerySource}; +use crate::result::QueryResult; + +/// Types which can be passed to +/// [`update.set`](struct.UpdateStatement.html#method.set). +/// +/// This trait can be [derived](derive.AsChangeset.html) +pub trait AsChangeset { + /// The table which `Self::Changeset` will be updating + type Target: QuerySource; + + /// The update statement this type represents + type Changeset; + + /// Convert `self` into the actual update statement being executed + fn as_changeset(self) -> Self::Changeset; +} + +#[doc(inline)] +pub use diesel_derives::AsChangeset; + +impl AsChangeset for Option { + type Target = T::Target; + type Changeset = Option; + + fn as_changeset(self) -> Self::Changeset { + self.map(AsChangeset::as_changeset) + } +} + +impl AsChangeset for Eq +where + Left: Column, + Right: AppearsOnTable, +{ + type Target = Left::Table; + type Changeset = Assign; + + fn as_changeset(self) -> Self::Changeset { + Assign { + _column: self.left, + expr: self.right, + } + } +} + +impl AsChangeset for Grouped> +where + Eq: AsChangeset, +{ + type Target = as AsChangeset>::Target; + + type Changeset = as AsChangeset>::Changeset; + + fn as_changeset(self) -> Self::Changeset { + self.0.as_changeset() + } +} + +#[derive(Debug, Clone, Copy)] +pub struct Assign { + _column: Col, + expr: Expr, +} + +impl QueryFragment for Assign +where + DB: Backend, + T: Column, + U: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_identifier(T::NAME)?; + out.push_sql(" = "); + QueryFragment::walk_ast(&self.expr, out) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/update_statement/mod.rs b/collector/benchmarks/diesel/diesel/src/query_builder/update_statement/mod.rs new file mode 100644 index 000000000..dd1d44359 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/update_statement/mod.rs @@ -0,0 +1,288 @@ +pub mod changeset; +pub mod target; + +pub use self::changeset::AsChangeset; +pub use self::target::{IntoUpdateTarget, UpdateTarget}; + +use crate::backend::Backend; +use crate::dsl::{Filter, IntoBoxed}; +use crate::expression::{ + is_aggregate, AppearsOnTable, Expression, MixedAggregates, SelectableExpression, ValidGrouping, +}; +use crate::query_builder::returning_clause::*; +use crate::query_builder::where_clause::*; +use crate::query_builder::*; +use crate::query_dsl::methods::{BoxedDsl, FilterDsl}; +use crate::query_dsl::RunQueryDsl; +use crate::query_source::Table; +use crate::result::Error::QueryBuilderError; +use crate::result::QueryResult; + +impl UpdateStatement { + pub(crate) fn new(target: UpdateTarget) -> Self { + UpdateStatement { + table: target.table, + where_clause: target.where_clause, + values: SetNotCalled, + returning: NoReturningClause, + } + } + + /// Provides the `SET` clause of the `UPDATE` statement. + /// + /// See [`update`](../fn.update.html) for usage examples, or [the update + /// guide](https://diesel.rs/guides/all-about-updates/) for a more exhaustive + /// set of examples. + pub fn set(self, values: V) -> UpdateStatement + where + T: Table, + V: changeset::AsChangeset, + UpdateStatement: AsQuery, + { + UpdateStatement { + table: self.table, + where_clause: self.where_clause, + values: values.as_changeset(), + returning: self.returning, + } + } +} + +#[derive(Debug, Copy, Clone)] +#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."] +/// Represents a complete `UPDATE` statement. +/// +/// See [`update`](../fn.update.html) for usage examples, or [the update +/// guide](https://diesel.rs/guides/all-about-updates/) for a more exhaustive +/// set of examples. +pub struct UpdateStatement { + table: T, + where_clause: U, + values: V, + returning: Ret, +} + +/// An `UPDATE` statement with a boxed `WHERE` clause. +pub type BoxedUpdateStatement<'a, DB, T, V = SetNotCalled, Ret = NoReturningClause> = + UpdateStatement, V, Ret>; + +impl UpdateStatement { + /// Adds the given predicate to the `WHERE` clause of the statement being + /// constructed. + /// + /// If there is already a `WHERE` clause, the predicate will be appended + /// with `AND`. There is no difference in behavior between + /// `update(table.filter(x))` and `update(table).filter(x)`. + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let updated_rows = diesel::update(users) + /// .set(name.eq("Jim")) + /// .filter(name.eq("Sean")) + /// .execute(&connection); + /// assert_eq!(Ok(1), updated_rows); + /// + /// let expected_names = vec!["Jim".to_string(), "Tess".to_string()]; + /// let names = users.select(name).order(id).load(&connection); + /// + /// assert_eq!(Ok(expected_names), names); + /// # } + /// ``` + pub fn filter(self, predicate: Predicate) -> Filter + where + Self: FilterDsl, + { + FilterDsl::filter(self, predicate) + } + + /// Boxes the `WHERE` clause of this update statement. + /// + /// This is useful for cases where you want to conditionally modify a query, + /// but need the type to remain the same. The backend must be specified as + /// part of this. It is not possible to box a query and have it be useable + /// on multiple backends. + /// + /// A boxed query will incur a minor performance penalty, as the query builder + /// can no longer be inlined by the compiler. For most applications this cost + /// will be minimal. + /// + /// ### Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use std::collections::HashMap; + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// # let mut params = HashMap::new(); + /// # params.insert("tess_has_been_a_jerk", false); + /// let mut query = diesel::update(users) + /// .set(name.eq("Jerk")) + /// .into_boxed(); + /// + /// if !params["tess_has_been_a_jerk"] { + /// query = query.filter(name.ne("Tess")); + /// } + /// + /// let updated_rows = query.execute(&connection)?; + /// assert_eq!(1, updated_rows); + /// + /// let expected_names = vec!["Jerk", "Tess"]; + /// let names = users.select(name).order(id).load::(&connection)?; + /// + /// assert_eq!(expected_names, names); + /// # Ok(()) + /// # } + /// ``` + pub fn into_boxed<'a, DB>(self) -> IntoBoxed<'a, Self, DB> + where + DB: Backend, + Self: BoxedDsl<'a, DB>, + { + BoxedDsl::internal_into_boxed(self) + } +} + +impl FilterDsl for UpdateStatement +where + U: WhereAnd, + Predicate: AppearsOnTable, +{ + type Output = UpdateStatement; + + fn filter(self, predicate: Predicate) -> Self::Output { + UpdateStatement { + table: self.table, + where_clause: self.where_clause.and(predicate), + values: self.values, + returning: self.returning, + } + } +} + +impl<'a, T, U, V, Ret, DB> BoxedDsl<'a, DB> for UpdateStatement +where + U: Into>, +{ + type Output = BoxedUpdateStatement<'a, DB, T, V, Ret>; + + fn internal_into_boxed(self) -> Self::Output { + UpdateStatement { + table: self.table, + where_clause: self.where_clause.into(), + values: self.values, + returning: self.returning, + } + } +} + +impl QueryFragment for UpdateStatement +where + DB: Backend, + T: Table, + T::FromClause: QueryFragment, + U: QueryFragment, + V: QueryFragment, + Ret: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + if self.values.is_noop()? { + return Err(QueryBuilderError( + "There are no changes to save. This query cannot be built".into(), + )); + } + + out.unsafe_to_cache_prepared(); + out.push_sql("UPDATE "); + self.table.from_clause().walk_ast(out.reborrow())?; + out.push_sql(" SET "); + self.values.walk_ast(out.reborrow())?; + self.where_clause.walk_ast(out.reborrow())?; + self.returning.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl QueryId for UpdateStatement { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl AsQuery for UpdateStatement +where + T: Table, + UpdateStatement>: Query, + T::AllColumns: ValidGrouping<()>, + >::IsAggregate: + MixedAggregates, +{ + type SqlType = ::SqlType; + type Query = UpdateStatement>; + + fn as_query(self) -> Self::Query { + self.returning(T::all_columns()) + } +} + +impl Query for UpdateStatement> +where + T: Table, + Ret: Expression + SelectableExpression + ValidGrouping<()>, + Ret::IsAggregate: MixedAggregates, +{ + type SqlType = Ret::SqlType; +} + +impl RunQueryDsl for UpdateStatement {} + +impl UpdateStatement { + /// Specify what expression is returned after execution of the `update`. + /// # Examples + /// + /// ### Updating a single record: + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # #[cfg(feature = "postgres")] + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let updated_name = diesel::update(users.filter(id.eq(1))) + /// .set(name.eq("Dean")) + /// .returning(name) + /// .get_result(&connection); + /// assert_eq!(Ok("Dean".to_string()), updated_name); + /// # } + /// # #[cfg(not(feature = "postgres"))] + /// # fn main() {} + /// ``` + pub fn returning(self, returns: E) -> UpdateStatement> + where + T: Table, + UpdateStatement>: Query, + { + UpdateStatement { + table: self.table, + where_clause: self.where_clause, + values: self.values, + returning: ReturningClause(returns), + } + } +} + +/// Indicates that you have not yet called `.set` on an update statement +#[derive(Debug, Clone, Copy)] +pub struct SetNotCalled; diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/update_statement/target.rs b/collector/benchmarks/diesel/diesel/src/query_builder/update_statement/target.rs new file mode 100644 index 000000000..a76132503 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/update_statement/target.rs @@ -0,0 +1,47 @@ +use crate::associations::{HasTable, Identifiable}; +use crate::dsl::Find; +use crate::query_dsl::methods::FindDsl; +use crate::query_source::Table; + +#[doc(hidden)] +#[derive(Debug)] +pub struct UpdateTarget { + pub table: Table, + pub where_clause: WhereClause, +} + +/// A type which can be passed to [`update`] or [`delete`]. +/// +/// Apps will never need to implement this type directly. There are three kinds +/// which implement this trait. Tables, queries which have only had `filter` +/// called on them, and types which implement `Identifiable`. +/// +/// When a table is passed to `update`, every row in the table will be updated. +/// You can scope this down by calling [`filter`] which will +/// result in `UPDATE your_table SET ... WHERE args_to_filter`. Passing a type +/// which implements `Identifiable` is the same as passing +/// `SomeStruct::table().find(some_struct)`. +/// +/// [`update`]: ../fn.update.html +/// [`delete`]: ../fn.delete.html +/// [`filter`]: struct.UpdateStatement.html#method.filter +pub trait IntoUpdateTarget: HasTable { + /// What is the `WHERE` clause of this target? + type WhereClause; + + /// Decomposes `self` into the table and where clause. + fn into_update_target(self) -> UpdateTarget; +} + +impl IntoUpdateTarget for T +where + T: Identifiable
, + Tab: Table + FindDsl, + Find: IntoUpdateTarget
, +{ + type WhereClause = V; + + fn into_update_target(self) -> UpdateTarget { + T::table().find(self.id()).into_update_target() + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/upsert/into_conflict_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/into_conflict_clause.rs new file mode 100644 index 000000000..892886b5e --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/into_conflict_clause.rs @@ -0,0 +1,124 @@ +use crate::insertable::{BatchInsert, OwnedBatchInsert}; +use crate::query_builder::insert_statement::InsertFromSelect; +#[cfg(feature = "sqlite")] +use crate::query_builder::where_clause::{BoxedWhereClause, WhereClause}; +#[cfg(any(feature = "sqlite", feature = "postgres"))] +use crate::query_builder::{AstPass, QueryFragment}; +use crate::query_builder::{BoxedSelectStatement, Query, SelectStatement, ValuesClause}; +#[cfg(any(feature = "sqlite", feature = "postgres"))] +use crate::result::QueryResult; + +pub trait IntoConflictValueClause { + type ValueClause; + + fn into_value_clause(self) -> Self::ValueClause; +} + +#[derive(Debug, Clone, Copy)] +pub struct OnConflictSelectWrapper(S); + +impl Query for OnConflictSelectWrapper +where + Q: Query, +{ + type SqlType = Q::SqlType; +} + +#[cfg(feature = "postgres")] +impl QueryFragment for OnConflictSelectWrapper +where + S: QueryFragment, +{ + fn walk_ast(&self, out: AstPass) -> QueryResult<()> { + self.0.walk_ast(out) + } +} + +// The corresponding impl for`NoWhereClause` is missing because of +// https://www.sqlite.org/lang_UPSERT.html (Parsing Ambiguity) +#[cfg(feature = "sqlite")] +impl QueryFragment + for OnConflictSelectWrapper, O, LOf, G, LC>> +where + SelectStatement, O, LOf, G, LC>: QueryFragment, +{ + fn walk_ast(&self, out: AstPass) -> QueryResult<()> { + self.0.walk_ast(out) + } +} + +#[cfg(feature = "sqlite")] +impl<'a, ST, QS> QueryFragment + for OnConflictSelectWrapper> +where + BoxedSelectStatement<'a, ST, QS, crate::sqlite::Sqlite>: QueryFragment, + QS: crate::query_source::QuerySource, + QS::FromClause: QueryFragment, +{ + fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + // https://www.sqlite.org/lang_UPSERT.html (Parsing Ambiguity) + self.0.build_query(pass, |where_clause, mut pass| { + match where_clause { + BoxedWhereClause::None => pass.push_sql(" WHERE 1=1 "), + w => w.walk_ast(pass.reborrow())?, + } + Ok(()) + }) + } +} + +impl IntoConflictValueClause for ValuesClause { + type ValueClause = Self; + + fn into_value_clause(self) -> Self::ValueClause { + self + } +} + +impl<'a, Inner, Tab> IntoConflictValueClause for BatchInsert<'a, Inner, Tab> { + type ValueClause = Self; + + fn into_value_clause(self) -> Self::ValueClause { + self + } +} + +impl IntoConflictValueClause for OwnedBatchInsert { + type ValueClause = Self; + + fn into_value_clause(self) -> Self::ValueClause { + self + } +} + +impl IntoConflictValueClause + for InsertFromSelect, Columns> +{ + type ValueClause = InsertFromSelect< + OnConflictSelectWrapper>, + Columns, + >; + + fn into_value_clause(self) -> Self::ValueClause { + let InsertFromSelect { columns, query } = self; + InsertFromSelect { + query: OnConflictSelectWrapper(query), + columns, + } + } +} + +impl<'a, ST, QS, DB, Columns> IntoConflictValueClause + for InsertFromSelect, Columns> +{ + type ValueClause = + InsertFromSelect>, Columns>; + + fn into_value_clause(self) -> Self::ValueClause { + let InsertFromSelect { columns, query } = self; + InsertFromSelect { + query: OnConflictSelectWrapper(query), + columns, + } + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/upsert/mod.rs b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/mod.rs new file mode 100644 index 000000000..25122db3b --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/mod.rs @@ -0,0 +1,5 @@ +pub(crate) mod into_conflict_clause; +pub(crate) mod on_conflict_actions; +pub(crate) mod on_conflict_clause; +pub(crate) mod on_conflict_target; +pub(crate) mod on_conflict_target_decorations; diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_actions.rs b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_actions.rs new file mode 100644 index 000000000..3a3d71f6c --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_actions.rs @@ -0,0 +1,84 @@ +use crate::backend::{Backend, SupportsOnConflictClause}; +use crate::expression::{AppearsOnTable, Expression}; +use crate::query_builder::*; +use crate::query_source::*; +use crate::result::QueryResult; + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct DoNothing; + +impl QueryFragment for DoNothing +where + DB: Backend + SupportsOnConflictClause, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" DO NOTHING"); + Ok(()) + } +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct DoUpdate { + changeset: T, +} + +impl DoUpdate { + pub(crate) fn new(changeset: T) -> Self { + DoUpdate { changeset } + } +} + +impl QueryFragment for DoUpdate +where + DB: Backend + SupportsOnConflictClause, + T: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + if self.changeset.is_noop()? { + out.push_sql(" DO NOTHING"); + } else { + out.push_sql(" DO UPDATE SET "); + self.changeset.walk_ast(out.reborrow())?; + } + Ok(()) + } +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct Excluded(T); + +impl Excluded { + pub(crate) fn new(t: T) -> Self { + Excluded(t) + } +} + +impl QueryFragment for Excluded +where + DB: Backend + SupportsOnConflictClause, + T: Column, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("excluded."); + out.push_identifier(T::NAME)?; + Ok(()) + } +} + +impl Expression for Excluded +where + T: Expression, +{ + type SqlType = T::SqlType; +} + +impl AppearsOnTable for Excluded +where + T: Column, + Excluded: Expression, +{ +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_clause.rs new file mode 100644 index 000000000..95246cdd7 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_clause.rs @@ -0,0 +1,57 @@ +use super::on_conflict_actions::*; +use super::on_conflict_target::*; +use crate::backend::{Backend, SupportsOnConflictClause}; +use crate::insertable::*; +use crate::query_builder::*; +use crate::result::QueryResult; + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct OnConflictValues { + values: Values, + target: Target, + action: Action, +} + +impl OnConflictValues { + pub(crate) fn do_nothing(values: Values) -> Self { + Self::new(values, NoConflictTarget, DoNothing) + } +} + +impl OnConflictValues { + pub(crate) fn new(values: Values, target: Target, action: Action) -> Self { + OnConflictValues { + values, + target, + action, + } + } +} + +impl CanInsertInSingleQuery + for OnConflictValues +where + DB: Backend + SupportsOnConflictClause, + Values: CanInsertInSingleQuery, +{ + fn rows_to_insert(&self) -> Option { + self.values.rows_to_insert() + } +} + +impl QueryFragment for OnConflictValues +where + DB: Backend + SupportsOnConflictClause, + Values: QueryFragment, + Target: QueryFragment, + Action: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.values.walk_ast(out.reborrow())?; + out.push_sql(" ON CONFLICT"); + self.target.walk_ast(out.reborrow())?; + self.action.walk_ast(out.reborrow())?; + Ok(()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_target.rs b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_target.rs new file mode 100644 index 000000000..45028d46c --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_target.rs @@ -0,0 +1,106 @@ +use crate::backend::{Backend, SupportsOnConflictClause}; +use crate::expression::SqlLiteral; +use crate::query_builder::*; +use crate::query_source::Column; +use crate::result::QueryResult; + +#[doc(hidden)] +pub trait OnConflictTarget
{} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct NoConflictTarget; + +impl QueryFragment for NoConflictTarget +where + DB: Backend + SupportsOnConflictClause, +{ + fn walk_ast(&self, _: AstPass) -> QueryResult<()> { + Ok(()) + } +} + +impl
OnConflictTarget
for NoConflictTarget {} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct ConflictTarget(pub T); + +impl QueryFragment for ConflictTarget +where + DB: Backend + SupportsOnConflictClause, + T: Column, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" ("); + out.push_identifier(T::NAME)?; + out.push_sql(")"); + Ok(()) + } +} + +impl OnConflictTarget for ConflictTarget where T: Column {} + +impl QueryFragment for ConflictTarget> +where + DB: Backend + SupportsOnConflictClause, + SqlLiteral: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" "); + self.0.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl OnConflictTarget for ConflictTarget> {} + +impl QueryFragment for ConflictTarget<(T,)> +where + DB: Backend + SupportsOnConflictClause, + T: Column, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" ("); + out.push_identifier(T::NAME)?; + out.push_sql(")"); + Ok(()) + } +} + +impl OnConflictTarget for ConflictTarget<(T,)> where T: Column {} + +macro_rules! on_conflict_tuples { + ($( + $Tuple:tt { + $(($idx:tt) -> $T:ident, $ST:ident, $TT:ident,)* + } + )+) => { + $( + impl<_DB, _T, $($T),*> QueryFragment<_DB> for ConflictTarget<(_T, $($T),*)> where + _DB: Backend + SupportsOnConflictClause, + _T: Column, + $($T: Column,)* + { + fn walk_ast(&self, mut out: AstPass<_DB>) -> QueryResult<()> { + out.push_sql(" ("); + out.push_identifier(_T::NAME)?; + $( + out.push_sql(", "); + out.push_identifier($T::NAME)?; + )* + out.push_sql(")"); + Ok(()) + } + } + + impl<_T, $($T),*> OnConflictTarget<_T::Table> for ConflictTarget<(_T, $($T),*)> where + _T: Column, + $($T: Column,)* + { + } + )* + } +} + +__diesel_for_each_tuple!(on_conflict_tuples); diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_target_decorations.rs b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_target_decorations.rs new file mode 100644 index 000000000..a510723ee --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_target_decorations.rs @@ -0,0 +1,71 @@ +use crate::backend::{Backend, SupportsOnConflictClause, SupportsOnConflictTargetDecorations}; +use crate::expression::Expression; +use crate::query_builder::upsert::on_conflict_target::{ConflictTarget, NoConflictTarget}; +use crate::query_builder::where_clause::{NoWhereClause, WhereAnd, WhereClause}; +use crate::query_builder::{AstPass, QueryFragment, QueryResult}; +use crate::sql_types::BoolOrNullableBool; + +pub trait UndecoratedConflictTarget {} + +impl UndecoratedConflictTarget for NoConflictTarget {} +impl UndecoratedConflictTarget for ConflictTarget {} + +/// Interface to add information to conflict targets. +/// Designed to be open for further additions to conflict targets like constraints +pub trait DecoratableTarget

{ + /// Output type of filter_target operation + type FilterOutput; + /// equivalent to filter of FilterDsl but aimed at conflict targets + fn filter_target(self, predicate: P) -> Self::FilterOutput; +} + +#[derive(Debug)] +pub struct DecoratedConflictTarget { + target: T, + where_clause: U, +} + +impl DecoratableTarget

for T +where + P: Expression, + P::SqlType: BoolOrNullableBool, + T: UndecoratedConflictTarget, +{ + type FilterOutput = DecoratedConflictTarget>; + + fn filter_target(self, predicate: P) -> Self::FilterOutput { + DecoratedConflictTarget { + target: self, + where_clause: NoWhereClause.and(predicate), + } + } +} + +impl DecoratableTarget

for DecoratedConflictTarget +where + P: Expression, + P::SqlType: BoolOrNullableBool, + U: WhereAnd

, +{ + type FilterOutput = DecoratedConflictTarget>::Output>; + + fn filter_target(self, predicate: P) -> Self::FilterOutput { + DecoratedConflictTarget { + target: self.target, + where_clause: self.where_clause.and(predicate), + } + } +} + +impl QueryFragment for DecoratedConflictTarget +where + T: QueryFragment, + U: QueryFragment, + DB: Backend + SupportsOnConflictClause + SupportsOnConflictTargetDecorations, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.target.walk_ast(out.reborrow())?; + self.where_clause.walk_ast(out.reborrow())?; + Ok(()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/where_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/where_clause.rs new file mode 100644 index 000000000..ac53f0da4 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/where_clause.rs @@ -0,0 +1,190 @@ +use super::*; +use crate::backend::Backend; +use crate::expression::grouped::Grouped; +use crate::expression::operators::{And, Or}; +use crate::expression::*; +use crate::result::QueryResult; +use crate::sql_types::BoolOrNullableBool; + +/// Add `Predicate` to the current `WHERE` clause, joining with `AND` if +/// applicable. +pub trait WhereAnd { + /// What is the type of the resulting `WHERE` clause? + type Output; + + /// See the trait-level docs. + fn and(self, predicate: Predicate) -> Self::Output; +} + +/// Add `Predicate` to the current `WHERE` clause, joining with `OR` if +/// applicable. +pub trait WhereOr { + /// What is the type of the resulting `WHERE` clause? + type Output; + + /// See the trait-level docs. + fn or(self, predicate: Predicate) -> Self::Output; +} + +/// Represents that a query has no `WHERE` clause. +#[derive(Debug, Clone, Copy, QueryId)] +pub struct NoWhereClause; + +impl QueryFragment for NoWhereClause { + fn walk_ast(&self, _: AstPass) -> QueryResult<()> { + Ok(()) + } +} + +impl WhereAnd for NoWhereClause +where + Predicate: Expression, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = WhereClause; + + fn and(self, predicate: Predicate) -> Self::Output { + WhereClause(predicate) + } +} + +impl WhereOr for NoWhereClause +where + Predicate: Expression, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = WhereClause; + + fn or(self, predicate: Predicate) -> Self::Output { + WhereClause(predicate) + } +} + +impl<'a, DB> Into> for NoWhereClause { + fn into(self) -> BoxedWhereClause<'a, DB> { + BoxedWhereClause::None + } +} + +/// The `WHERE` clause of a query. +#[derive(Debug, Clone, Copy, QueryId)] +pub struct WhereClause(Expr); + +impl QueryFragment for WhereClause +where + DB: Backend, + Expr: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" WHERE "); + self.0.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl WhereAnd for WhereClause +where + Expr: Expression, + Expr::SqlType: BoolOrNullableBool, + Predicate: Expression, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = WhereClause>>; + + fn and(self, predicate: Predicate) -> Self::Output { + WhereClause(Grouped(And::new(self.0, predicate))) + } +} + +impl WhereOr for WhereClause +where + Expr: Expression, + Expr::SqlType: BoolOrNullableBool, + Predicate: Expression, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = WhereClause>>; + + fn or(self, predicate: Predicate) -> Self::Output { + WhereClause(Grouped(Or::new(self.0, predicate))) + } +} + +impl<'a, DB, Predicate> Into> for WhereClause +where + DB: Backend, + Predicate: QueryFragment + Send + 'a, +{ + fn into(self) -> BoxedWhereClause<'a, DB> { + BoxedWhereClause::Where(Box::new(self.0)) + } +} + +/// Marker trait indicating that a `WHERE` clause is valid for a given query +/// source. +pub trait ValidWhereClause {} + +impl ValidWhereClause for NoWhereClause {} + +impl ValidWhereClause for WhereClause where Expr: AppearsOnTable {} + +#[allow(missing_debug_implementations)] // We can't... +pub enum BoxedWhereClause<'a, DB> { + Where(Box + Send + 'a>), + None, +} + +impl<'a, DB> QueryFragment for BoxedWhereClause<'a, DB> +where + DB: Backend, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + match *self { + BoxedWhereClause::Where(ref where_clause) => { + out.push_sql(" WHERE "); + where_clause.walk_ast(out) + } + BoxedWhereClause::None => Ok(()), + } + } +} + +impl<'a, DB> QueryId for BoxedWhereClause<'a, DB> { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl<'a, DB, Predicate> WhereAnd for BoxedWhereClause<'a, DB> +where + DB: Backend + 'a, + Predicate: QueryFragment + Send + 'a, +{ + type Output = Self; + + fn and(self, predicate: Predicate) -> Self::Output { + use self::BoxedWhereClause::Where; + + match self { + Where(where_clause) => Where(Box::new(Grouped(And::new(where_clause, predicate)))), + BoxedWhereClause::None => Where(Box::new(predicate)), + } + } +} + +impl<'a, DB, Predicate> WhereOr for BoxedWhereClause<'a, DB> +where + DB: Backend + 'a, + Predicate: QueryFragment + Send + 'a, +{ + type Output = Self; + + fn or(self, predicate: Predicate) -> Self::Output { + use self::BoxedWhereClause::Where; + + match self { + Where(where_clause) => Where(Box::new(Grouped(Or::new(where_clause, predicate)))), + BoxedWhereClause::None => Where(Box::new(predicate)), + } + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/belonging_to_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/belonging_to_dsl.rs new file mode 100644 index 000000000..1c00c6447 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/belonging_to_dsl.rs @@ -0,0 +1,54 @@ +/// Constructs a query that finds record(s) based on directional association with other record(s). +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use schema::{posts, users}; +/// # +/// # #[derive(Identifiable, Queryable)] +/// # pub struct User { +/// # id: i32, +/// # name: String, +/// # } +/// # +/// # #[derive(Debug, PartialEq)] +/// # #[derive(Identifiable, Queryable, Associations)] +/// # #[belongs_to(User)] +/// # pub struct Post { +/// # id: i32, +/// # user_id: i32, +/// # title: String, +/// # } +/// # +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let connection = establish_connection(); +/// # use self::users::dsl::*; +/// # use self::posts::dsl::{posts, title}; +/// let sean = users.filter(name.eq("Sean")).first::(&connection)?; +/// let tess = users.filter(name.eq("Tess")).first::(&connection)?; +/// +/// let seans_posts = Post::belonging_to(&sean) +/// .select(title) +/// .load::(&connection)?; +/// assert_eq!(vec!["My first post", "About Rust"], seans_posts); +/// +/// // A vec or slice can be passed as well +/// let more_posts = Post::belonging_to(&vec![sean, tess]) +/// .select(title) +/// .load::(&connection)?; +/// assert_eq!(vec!["My first post", "About Rust", "My first post too"], more_posts); +/// # Ok(()) +/// # } +/// ``` +pub trait BelongingToDsl { + /// The query returned by `belonging_to` + type Output; + + /// Get the record(s) belonging to record(s) `other` + fn belonging_to(other: T) -> Self::Output; +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/boxed_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/boxed_dsl.rs new file mode 100644 index 000000000..701bcce37 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/boxed_dsl.rs @@ -0,0 +1,36 @@ +use crate::dsl; +use crate::expression::TypedExpressionType; +use crate::expression::ValidGrouping; +use crate::query_builder::AsQuery; +use crate::query_builder::SelectStatement; +use crate::query_source::Table; +use crate::Expression; + +/// The `into_boxed` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `into_boxed` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait BoxedDsl<'a, DB> { + /// The return type of `internal_into_boxed` + type Output; + + /// See the trait documentation. + fn internal_into_boxed(self) -> dsl::IntoBoxed<'a, Self, DB>; +} + +impl<'a, T, DB> BoxedDsl<'a, DB> for T +where + T: Table + AsQuery>, + SelectStatement: BoxedDsl<'a, DB>, + T::DefaultSelection: Expression + ValidGrouping<()>, + T::SqlType: TypedExpressionType, +{ + type Output = dsl::IntoBoxed<'a, SelectStatement, DB>; + + fn internal_into_boxed(self) -> Self::Output { + self.as_query().internal_into_boxed() + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/distinct_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/distinct_dsl.rs new file mode 100644 index 000000000..85dbd2e4c --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/distinct_dsl.rs @@ -0,0 +1,68 @@ +use crate::dsl; +#[cfg(feature = "postgres")] +use crate::expression::SelectableExpression; +use crate::expression::TypedExpressionType; +use crate::expression::ValidGrouping; +use crate::query_builder::AsQuery; +use crate::query_builder::SelectStatement; +use crate::query_source::Table; +use crate::Expression; + +/// The `distinct` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `distinct` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait DistinctDsl { + /// The type returned by `.distinct` + type Output; + + /// See the trait documentation. + fn distinct(self) -> dsl::Distinct; +} + +impl DistinctDsl for T +where + T: Table + AsQuery>, + T::DefaultSelection: Expression + ValidGrouping<()>, + T::SqlType: TypedExpressionType, +{ + type Output = dsl::Distinct>; + + fn distinct(self) -> dsl::Distinct> { + self.as_query().distinct() + } +} + +/// The `distinct_on` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `distinct_on` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +#[cfg(feature = "postgres")] +pub trait DistinctOnDsl { + /// The type returned by `.distinct_on` + type Output; + + /// See the trait documentation + fn distinct_on(self, selection: Selection) -> dsl::DistinctOn; +} + +#[cfg(feature = "postgres")] +impl DistinctOnDsl for T +where + Selection: SelectableExpression, + T: Table + AsQuery>, + T::DefaultSelection: Expression + ValidGrouping<()>, + T::SqlType: TypedExpressionType, +{ + type Output = dsl::DistinctOn, Selection>; + + fn distinct_on(self, selection: Selection) -> dsl::DistinctOn { + self.as_query().distinct_on(selection) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/filter_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/filter_dsl.rs new file mode 100644 index 000000000..9247b6abd --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/filter_dsl.rs @@ -0,0 +1,85 @@ +use crate::dsl::{Filter, OrFilter}; +use crate::expression_methods::*; +use crate::query_source::*; + +/// The `filter` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `filter` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait FilterDsl { + /// The type returned by `.filter`. + type Output; + + /// See the trait documentation. + fn filter(self, predicate: Predicate) -> Self::Output; +} + +impl FilterDsl for T +where + T: Table, + T::Query: FilterDsl, +{ + type Output = Filter; + + fn filter(self, predicate: Predicate) -> Self::Output { + self.as_query().filter(predicate) + } +} + +/// The `find` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `find` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait FindDsl { + /// The type returned by `.find`. + type Output; + + /// See the trait documentation. + fn find(self, id: PK) -> Self::Output; +} + +impl FindDsl for T +where + T: Table + FilterDsl<<::PrimaryKey as EqAll>::Output>, + T::PrimaryKey: EqAll, +{ + type Output = Filter>::Output>; + + fn find(self, id: PK) -> Self::Output { + let primary_key = self.primary_key(); + self.filter(primary_key.eq_all(id)) + } +} + +/// The `or_filter` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `or_filter` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait OrFilterDsl { + /// The type returned by `.filter`. + type Output; + + /// See the trait documentation. + fn or_filter(self, predicate: Predicate) -> Self::Output; +} + +impl OrFilterDsl for T +where + T: Table, + T::Query: OrFilterDsl, +{ + type Output = OrFilter; + + fn or_filter(self, predicate: Predicate) -> Self::Output { + self.as_query().or_filter(predicate) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/group_by_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/group_by_dsl.rs new file mode 100644 index 000000000..517ec2b32 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/group_by_dsl.rs @@ -0,0 +1,35 @@ +use crate::dsl; +use crate::expression::Expression; +use crate::expression::TypedExpressionType; +use crate::expression::ValidGrouping; +use crate::query_builder::{AsQuery, SelectStatement}; +use crate::query_source::Table; + +/// The `group_by` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `group_by` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait GroupByDsl { + /// The type returned by `.group_by` + type Output; + + /// See the trait documentation. + fn group_by(self, expr: Expr) -> dsl::GroupBy; +} + +impl GroupByDsl for T +where + Expr: Expression, + T: Table + AsQuery>, + T::DefaultSelection: Expression + ValidGrouping<()>, + T::SqlType: TypedExpressionType, +{ + type Output = dsl::GroupBy, Expr>; + + fn group_by(self, expr: Expr) -> dsl::GroupBy { + self.as_query().group_by(expr) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/join_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/join_dsl.rs new file mode 100644 index 000000000..31b740dfc --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/join_dsl.rs @@ -0,0 +1,80 @@ +use crate::query_builder::AsQuery; +use crate::query_source::joins::OnClauseWrapper; +use crate::query_source::{JoinTo, QuerySource, Table}; + +#[doc(hidden)] +/// `JoinDsl` support trait to emulate associated type constructors +pub trait InternalJoinDsl { + type Output; + + fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output; +} + +impl InternalJoinDsl for T +where + T: Table + AsQuery, + T::Query: InternalJoinDsl, +{ + type Output = >::Output; + + fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output { + self.as_query().join(rhs, kind, on) + } +} + +#[doc(hidden)] +/// `JoinDsl` support trait to emulate associated type constructors and grab +/// the known on clause from the associations API +pub trait JoinWithImplicitOnClause { + type Output; + + fn join_with_implicit_on_clause(self, rhs: Rhs, kind: Kind) -> Self::Output; +} + +impl JoinWithImplicitOnClause for Lhs +where + Lhs: JoinTo, + Lhs: InternalJoinDsl<>::FromClause, Kind, >::OnClause>, +{ + type Output = >::Output; + + fn join_with_implicit_on_clause(self, rhs: Rhs, kind: Kind) -> Self::Output { + let (from, on) = Lhs::join_target(rhs); + self.join(from, kind, on) + } +} + +/// Specify the `ON` clause for a join statement. This will override +/// any implicit `ON` clause that would come from [`joinable!`] +/// +/// [`joinable!`]: ../macro.joinable.html +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use schema::{users, posts}; +/// # +/// # fn main() { +/// # let connection = establish_connection(); +/// let data = users::table +/// .left_join(posts::table.on( +/// users::id.eq(posts::user_id).and( +/// posts::title.eq("My first post")) +/// )) +/// .select((users::name, posts::title.nullable())) +/// .load(&connection); +/// let expected = vec![ +/// ("Sean".to_string(), Some("My first post".to_string())), +/// ("Tess".to_string(), None), +/// ]; +/// assert_eq!(Ok(expected), data); +/// # } +pub trait JoinOnDsl: Sized { + /// See the trait documentation. + fn on(self, on: On) -> OnClauseWrapper { + OnClauseWrapper::new(self, on) + } +} + +impl JoinOnDsl for T {} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/limit_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/limit_dsl.rs new file mode 100644 index 000000000..b9f94dfb8 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/limit_dsl.rs @@ -0,0 +1,28 @@ +use crate::query_source::Table; + +/// The `limit` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `limit` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait LimitDsl { + /// The type returned by `.limit` + type Output; + + /// See the trait documentation + fn limit(self, limit: i64) -> Self::Output; +} + +impl LimitDsl for T +where + T: Table, + T::Query: LimitDsl, +{ + type Output = ::Output; + + fn limit(self, limit: i64) -> Self::Output { + self.as_query().limit(limit) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/load_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/load_dsl.rs new file mode 100644 index 000000000..21246de94 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/load_dsl.rs @@ -0,0 +1,57 @@ +use super::RunQueryDsl; +use crate::backend::Backend; +use crate::connection::Connection; +use crate::deserialize::FromSqlRow; +use crate::expression::QueryMetadata; +use crate::query_builder::{AsQuery, QueryFragment, QueryId}; +use crate::result::QueryResult; + +/// The `load` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`RunQueryDsl`]. However, you may need a where clause on this trait +/// to call `load` from generic code. +/// +/// [`RunQueryDsl`]: ../trait.RunQueryDsl.html +pub trait LoadQuery: RunQueryDsl { + /// Load this query + fn internal_load(self, conn: &Conn) -> QueryResult>; +} + +impl LoadQuery for T +where + Conn: Connection, + T: AsQuery + RunQueryDsl, + T::Query: QueryFragment + QueryId, + U: FromSqlRow, + Conn::Backend: QueryMetadata, +{ + fn internal_load(self, conn: &Conn) -> QueryResult> { + conn.load(self) + } +} + +/// The `execute` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`RunQueryDsl`]. However, you may need a where clause on this trait +/// to call `execute` from generic code. +/// +/// [`RunQueryDsl`]: ../trait.RunQueryDsl.html +pub trait ExecuteDsl, DB: Backend = ::Backend>: + Sized +{ + /// Execute this command + fn execute(query: Self, conn: &Conn) -> QueryResult; +} + +impl ExecuteDsl for T +where + Conn: Connection, + DB: Backend, + T: QueryFragment + QueryId, +{ + fn execute(query: Self, conn: &Conn) -> QueryResult { + conn.execute_returning_count(&query) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/locking_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/locking_dsl.rs new file mode 100644 index 000000000..c0de6e17f --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/locking_dsl.rs @@ -0,0 +1,55 @@ +use crate::expression::TypedExpressionType; +use crate::expression::ValidGrouping; +use crate::query_builder::AsQuery; +use crate::query_builder::SelectStatement; +use crate::query_source::Table; +use crate::Expression; + +/// Methods related to locking select statements +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `for_update` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait LockingDsl { + /// The type returned by `set_lock`. See [`dsl::ForUpdate`] and friends for + /// convenient access to this type. + /// + /// [`dsl::ForUpdate`]: ../../dsl/type.ForUpdate.html + type Output; + + /// See the trait level documentation + fn with_lock(self, lock: Lock) -> Self::Output; +} + +impl LockingDsl for T +where + T: Table + AsQuery>, + T::DefaultSelection: Expression + ValidGrouping<()>, + T::SqlType: TypedExpressionType, +{ + type Output = as LockingDsl>::Output; + + fn with_lock(self, lock: Lock) -> Self::Output { + self.as_query().with_lock(lock) + } +} + +/// Methods related to modifiers on locking select statements +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `skip_locked` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait ModifyLockDsl { + /// The type returned by `modify_lock`. See [`dsl::SkipLocked`] and friends + /// for convenient access to this type. + /// + /// [`dsl::SkipLocked`]: ../../dsl/type.SkipLocked.html + type Output; + + /// See the trait level documentation + fn modify_lock(self, modifier: Modifier) -> Self::Output; +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/mod.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/mod.rs new file mode 100644 index 000000000..d3c45ff47 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/mod.rs @@ -0,0 +1,1354 @@ +//! Traits that construct SELECT statements +//! +//! Traits in this module have methods that generally map to the keyword for the corresponding clause in SQL, +//! unless it conflicts with a Rust keyword (such as `WHERE`/`where`). +//! +//! Methods for constructing queries lives on the [`QueryDsl`] trait. +//! Methods for executing queries live on [`RunQueryDsl`]. +//! +//! See also [`expression_methods`][expression_methods] and [`dsl`][dsl]. +//! +//! [expression_methods]: ../expression_methods/index.html +//! [dsl]: ../dsl/index.html +//! [`QueryDsl`]: trait.QueryDsl.html +//! [`RunQueryDsl`]: trait.RunQueryDsl.html + +use crate::backend::Backend; +use crate::connection::Connection; +use crate::expression::count::CountStar; +use crate::expression::Expression; +use crate::helper_types::*; +use crate::query_builder::locking_clause as lock; +use crate::query_source::{joins, Table}; +use crate::result::{first_or_not_found, QueryResult}; + +mod belonging_to_dsl; +#[doc(hidden)] +pub mod boxed_dsl; +mod distinct_dsl; +#[doc(hidden)] +pub mod filter_dsl; +mod group_by_dsl; +mod join_dsl; +#[doc(hidden)] +pub mod limit_dsl; +#[doc(hidden)] +pub mod load_dsl; +mod locking_dsl; +mod nullable_select_dsl; +mod offset_dsl; +mod order_dsl; +mod save_changes_dsl; +#[doc(hidden)] +pub mod select_dsl; +mod single_value_dsl; + +pub use self::belonging_to_dsl::BelongingToDsl; +pub use self::join_dsl::{InternalJoinDsl, JoinOnDsl, JoinWithImplicitOnClause}; +#[doc(hidden)] +pub use self::load_dsl::LoadQuery; +pub use self::save_changes_dsl::{SaveChangesDsl, UpdateAndFetchResults}; + +/// The traits used by `QueryDsl`. +/// +/// Each trait in this module represents exactly one method from `QueryDsl`. +/// Apps should general rely on `QueryDsl` directly, rather than these traits. +/// However, generic code may need to include a where clause that references +/// these traits. +pub mod methods { + pub use super::boxed_dsl::BoxedDsl; + pub use super::distinct_dsl::*; + #[doc(inline)] + pub use super::filter_dsl::*; + pub use super::group_by_dsl::GroupByDsl; + pub use super::limit_dsl::LimitDsl; + pub use super::load_dsl::{ExecuteDsl, LoadQuery}; + pub use super::locking_dsl::{LockingDsl, ModifyLockDsl}; + pub use super::nullable_select_dsl::SelectNullableDsl; + pub use super::offset_dsl::OffsetDsl; + pub use super::order_dsl::{OrderDsl, ThenOrderDsl}; + pub use super::select_dsl::SelectDsl; + pub use super::single_value_dsl::SingleValueDsl; +} + +/// Methods used to construct select statements. +pub trait QueryDsl: Sized { + /// Adds the `DISTINCT` keyword to a query. + /// + /// This method will override any previous distinct clause that was present. + /// For example, on PostgreSQL, `foo.distinct_on(bar).distinct()` will + /// create the same query as `foo.distinct()`. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// # connection.execute("DELETE FROM users").unwrap(); + /// diesel::insert_into(users) + /// .values(&vec![name.eq("Sean"); 3]) + /// .execute(&connection)?; + /// let names = users.select(name).load::(&connection)?; + /// let distinct_names = users.select(name).distinct().load::(&connection)?; + /// + /// assert_eq!(vec!["Sean"; 3], names); + /// assert_eq!(vec!["Sean"; 1], distinct_names); + /// # Ok(()) + /// # } + /// ``` + fn distinct(self) -> Distinct + where + Self: methods::DistinctDsl, + { + methods::DistinctDsl::distinct(self) + } + + /// Adds the `DISTINCT ON` clause to a query. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::animals; + /// # + /// # #[derive(Queryable, Debug, PartialEq)] + /// # struct Animal { + /// # species: String, + /// # name: Option, + /// # legs: i32, + /// # } + /// # + /// # impl Animal { + /// # fn new>(species: S, name: Option<&str>, legs: i32) -> Self { + /// # Animal { + /// # species: species.into(), + /// # name: name.map(Into::into), + /// # legs + /// # } + /// # } + /// # } + /// # + /// # fn main() { + /// # use self::animals::dsl::*; + /// # let connection = establish_connection(); + /// # connection.execute("DELETE FROM animals").unwrap(); + /// diesel::insert_into(animals) + /// .values(&vec![ + /// (species.eq("dog"), name.eq(Some("Jack")), legs.eq(4)), + /// (species.eq("dog"), name.eq(None), legs.eq(4)), + /// (species.eq("spider"), name.eq(None), legs.eq(8)), + /// ]) + /// .execute(&connection) + /// .unwrap(); + /// let all_animals = animals.select((species, name, legs)).load(&connection); + /// let distinct_animals = animals.select((species, name, legs)).distinct_on(species).load(&connection); + /// + /// assert_eq!(Ok(vec![Animal::new("dog", Some("Jack"), 4), + /// Animal::new("dog", None, 4), + /// Animal::new("spider", None, 8)]), all_animals); + /// assert_eq!(Ok(vec![Animal::new("dog", Some("Jack"), 4), + /// Animal::new("spider", None, 8)]), distinct_animals); + /// # } + /// ``` + #[cfg(feature = "postgres")] + fn distinct_on(self, expr: Expr) -> DistinctOn + where + Self: methods::DistinctOnDsl, + { + methods::DistinctOnDsl::distinct_on(self, expr) + } + + // FIXME: Needs usage example and doc rewrite + /// Adds a `SELECT` clause to the query. + /// + /// If there was already a select clause present, it will be overridden. + /// For example, `foo.select(bar).select(baz)` will produce the same + /// query as `foo.select(baz)`. + /// + /// By default, the select clause will be roughly equivalent to `SELECT *` + /// (however, Diesel will list all columns to ensure that they are in the + /// order we expect). + /// + /// `select` has slightly stricter bounds on its arguments than other + /// methods. In particular, when used with a left outer join, `.nullable` + /// must be called on columns that come from the right side of a join. It + /// can be called on the column itself, or on an expression containing that + /// column. `title.nullable()`, `lower(title).nullable()`, and `(id, + /// title).nullable()` would all be valid. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::users; + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use self::users::dsl::*; + /// # let connection = establish_connection(); + /// // By default, all columns will be selected + /// let all_users = users.load::<(i32, String)>(&connection)?; + /// assert_eq!(vec![(1, String::from("Sean")), (2, String::from("Tess"))], all_users); + /// + /// let all_names = users.select(name).load::(&connection)?; + /// assert_eq!(vec!["Sean", "Tess"], all_names); + /// # Ok(()) + /// # } + /// ``` + /// + /// ### When used with a left join + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::{users, posts}; + /// # + /// # #[derive(Queryable, PartialEq, Eq, Debug)] + /// # struct User { + /// # id: i32, + /// # name: String, + /// # } + /// # + /// # impl User { + /// # fn new(id: i32, name: &str) -> Self { + /// # User { + /// # id, + /// # name: name.into(), + /// # } + /// # } + /// # } + /// # + /// # #[derive(Queryable, PartialEq, Eq, Debug)] + /// # struct Post { + /// # id: i32, + /// # user_id: i32, + /// # title: String, + /// # } + /// # + /// # impl Post { + /// # fn new(id: i32, user_id: i32, title: &str) -> Self { + /// # Post { + /// # id, + /// # user_id, + /// # title: title.into(), + /// # } + /// # } + /// # } + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # let connection = establish_connection(); + /// # connection.execute("DELETE FROM posts")?; + /// # diesel::insert_into(posts::table) + /// # .values((posts::user_id.eq(1), posts::title.eq("Sean's Post"))) + /// # .execute(&connection)?; + /// # let post_id = posts::table.select(posts::id) + /// # .first::(&connection)?; + /// let join = users::table.left_join(posts::table); + /// + /// // By default, all columns from both tables are selected + /// let all_data = join.load::<(User, Option)>(&connection)?; + /// let expected_data = vec![ + /// (User::new(1, "Sean"), Some(Post::new(post_id, 1, "Sean's Post"))), + /// (User::new(2, "Tess"), None), + /// ]; + /// assert_eq!(expected_data, all_data); + /// + /// // Since `posts` is on the right side of a left join, `.nullable` is + /// // needed. + /// let names_and_titles = join.select((users::name, posts::title.nullable())) + /// .load::<(String, Option)>(&connection)?; + /// let expected_data = vec![ + /// (String::from("Sean"), Some(String::from("Sean's Post"))), + /// (String::from("Tess"), None), + /// ]; + /// assert_eq!(expected_data, names_and_titles); + /// # Ok(()) + /// # } + /// ``` + fn select(self, selection: Selection) -> Select + where + Selection: Expression, + Self: methods::SelectDsl, + { + methods::SelectDsl::select(self, selection) + } + + /// Get the count of a query. This is equivalent to `.select(count_star())` + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let count = users.count().get_result(&connection); + /// assert_eq!(Ok(2), count); + /// # } + /// ``` + fn count(self) -> Select + where + Self: methods::SelectDsl, + { + use crate::dsl::count_star; + + QueryDsl::select(self, count_star()) + } + + /// Join two tables using a SQL `INNER JOIN`. + /// + /// If you have invoked [`joinable!`] for the two tables, you can pass that + /// table directly. Otherwise you will need to use [`.on`] to specify the `ON` + /// clause. + /// + /// [`joinable!`]: ../macro.joinable.html + /// [`.on`]: trait.JoinOnDsl.html#method.on + /// + /// You can join to as many tables as you'd like in a query, with the + /// restriction that no table can appear in the query more than once. The reason + /// for this restriction is that one of the appearances would require aliasing, + /// and we do not currently have a fleshed out story for dealing with table + /// aliases. + /// + /// You will also need to call [`allow_tables_to_appear_in_same_query!`]. + /// If you are using `diesel print-schema`, this will + /// have been generated for you. + /// See the documentation for [`allow_tables_to_appear_in_same_query!`] for + /// details. + /// + /// Diesel expects multi-table joins to be semantically grouped based on the + /// relationships. For example, `users.inner_join(posts.inner_join(comments))` + /// is not the same as `users.inner_join(posts).inner_join(comments)`. The first + /// would deserialize into `(User, (Post, Comment))` and generate the following + /// SQL: + /// + /// ```sql + /// SELECT * FROM users + /// INNER JOIN posts ON posts.user_id = users.id + /// INNER JOIN comments ON comments.post_id = posts.id + /// ``` + /// + /// While the second query would deserialize into `(User, Post, Comment)` and + /// generate the following SQL: + /// + /// ```sql + /// SELECT * FROM users + /// INNER JOIN posts ON posts.user_id = users.id + /// INNER JOIN comments ON comments.user_id = users.id + /// ``` + /// + /// [associations]: ../associations/index.html + /// [`allow_tables_to_appear_in_same_query!`]: ../macro.allow_tables_to_appear_in_same_query.html + /// + /// # Examples + /// + /// ### With implicit `ON` clause + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::{users, posts}; + /// # /* + /// joinable!(posts -> users (user_id)); + /// allow_tables_to_appear_in_same_query!(users, posts); + /// # */ + /// + /// # fn main() { + /// # use self::users::dsl::{users, name}; + /// # use self::posts::dsl::{posts, user_id, title}; + /// # let connection = establish_connection(); + /// let data = users.inner_join(posts) + /// .select((name, title)) + /// .load(&connection); + /// + /// let expected_data = vec![ + /// (String::from("Sean"), String::from("My first post")), + /// (String::from("Sean"), String::from("About Rust")), + /// (String::from("Tess"), String::from("My first post too")), + /// ]; + /// assert_eq!(Ok(expected_data), data); + /// # } + /// ``` + /// + /// ### With explicit `ON` clause + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::{users, posts}; + /// # + /// # /* + /// allow_tables_to_appear_in_same_query!(users, posts); + /// # */ + /// + /// # fn main() { + /// # use self::users::dsl::{users, name}; + /// # use self::posts::dsl::{posts, user_id, title}; + /// # let connection = establish_connection(); + /// diesel::insert_into(posts) + /// .values(&vec![ + /// (user_id.eq(1), title.eq("Sean's post")), + /// (user_id.eq(2), title.eq("Sean is a jerk")), + /// ]) + /// .execute(&connection) + /// .unwrap(); + /// + /// let data = users + /// .inner_join(posts.on(title.like(name.concat("%")))) + /// .select((name, title)) + /// .load(&connection); + /// let expected_data = vec![ + /// (String::from("Sean"), String::from("Sean's post")), + /// (String::from("Sean"), String::from("Sean is a jerk")), + /// ]; + /// assert_eq!(Ok(expected_data), data); + /// # } + /// ``` + fn inner_join(self, rhs: Rhs) -> InnerJoin + where + Self: JoinWithImplicitOnClause, + { + self.join_with_implicit_on_clause(rhs, joins::Inner) + } + + /// Join two tables using a SQL `LEFT OUTER JOIN`. + /// + /// Behaves similarly to [`inner_join`], but will produce a left join + /// instead. See [`inner_join`] for usage examples. + /// + /// [`inner_join`]: #method.inner_join + fn left_outer_join(self, rhs: Rhs) -> LeftJoin + where + Self: JoinWithImplicitOnClause, + { + self.join_with_implicit_on_clause(rhs, joins::LeftOuter) + } + + /// Alias for [`left_outer_join`]. + /// + /// [`left_outer_join`]: #method.left_outer_join + fn left_join(self, rhs: Rhs) -> LeftJoin + where + Self: JoinWithImplicitOnClause, + { + self.left_outer_join(rhs) + } + + /// Adds to the `WHERE` clause of a query. + /// + /// If there is already a `WHERE` clause, the result will be `old AND new`. + /// + /// # Example: + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let seans_id = users.filter(name.eq("Sean")).select(id) + /// .first(&connection); + /// assert_eq!(Ok(1), seans_id); + /// let tess_id = users.filter(name.eq("Tess")).select(id) + /// .first(&connection); + /// assert_eq!(Ok(2), tess_id); + /// # } + /// ``` + fn filter(self, predicate: Predicate) -> Filter + where + Self: methods::FilterDsl, + { + methods::FilterDsl::filter(self, predicate) + } + + /// Adds to the `WHERE` clause of a query using `OR` + /// + /// If there is already a `WHERE` clause, the result will be `(old OR new)`. + /// Calling `foo.filter(bar).or_filter(baz)` + /// is identical to `foo.filter(bar.or(baz))`. + /// However, the second form is much harder to do dynamically. + /// + /// # Example: + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// # diesel::delete(animals).execute(&connection)?; + /// diesel::insert_into(animals) + /// .values(&vec![ + /// (species.eq("cat"), legs.eq(4), name.eq("Sinatra")), + /// (species.eq("dog"), legs.eq(3), name.eq("Fido")), + /// (species.eq("spider"), legs.eq(8), name.eq("Charlotte")), + /// ]) + /// .execute(&connection)?; + /// + /// let good_animals = animals + /// .filter(name.eq("Fido")) + /// .or_filter(legs.eq(4)) + /// .select(name) + /// .get_results::>(&connection)?; + /// let expected = vec![ + /// Some(String::from("Sinatra")), + /// Some(String::from("Fido")), + /// ]; + /// assert_eq!(expected, good_animals); + /// # Ok(()) + /// # } + /// ``` + fn or_filter(self, predicate: Predicate) -> OrFilter + where + Self: methods::OrFilterDsl, + { + methods::OrFilterDsl::or_filter(self, predicate) + } + + /// Attempts to find a single record from the given table by primary key. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # use diesel::result::Error::NotFound; + /// # let connection = establish_connection(); + /// let sean = (1, "Sean".to_string()); + /// let tess = (2, "Tess".to_string()); + /// assert_eq!(Ok(sean), users.find(1).first(&connection)); + /// assert_eq!(Ok(tess), users.find(2).first(&connection)); + /// assert_eq!(Err::<(i32, String), _>(NotFound), users.find(3).first(&connection)); + /// # } + /// ``` + fn find(self, id: PK) -> Find + where + Self: methods::FindDsl, + { + methods::FindDsl::find(self, id) + } + + /// Sets the order clause of a query. + /// + /// If there was already an order clause, it will be overridden. See + /// also: + /// [`.desc()`](../expression_methods/trait.ExpressionMethods.html#method.desc) + /// and + /// [`.asc()`](../expression_methods/trait.ExpressionMethods.html#method.asc) + /// + /// Ordering by multiple columns can be achieved by passing a tuple of those + /// columns. + /// To construct an order clause of an unknown number of columns, + /// see [`QueryDsl::then_order_by`](#method.then_order_by) + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// # connection.execute("DELETE FROM users")?; + /// diesel::insert_into(users) + /// .values(&vec![ + /// name.eq("Saul"), + /// name.eq("Steve"), + /// name.eq("Stan"), + /// ]) + /// .execute(&connection)?; + /// + /// let ordered_names = users.select(name) + /// .order(name.desc()) + /// .load::(&connection)?; + /// assert_eq!(vec!["Steve", "Stan", "Saul"], ordered_names); + /// + /// diesel::insert_into(users).values(name.eq("Stan")).execute(&connection)?; + /// + /// let data = users.select((name, id)) + /// .order((name.asc(), id.desc())) + /// .load(&connection)?; + /// let expected_data = vec![ + /// (String::from("Saul"), 3), + /// (String::from("Stan"), 6), + /// (String::from("Stan"), 5), + /// (String::from("Steve"), 4), + /// ]; + /// assert_eq!(expected_data, data); + /// # Ok(()) + /// # } + /// ``` + fn order(self, expr: Expr) -> Order + where + Expr: Expression, + Self: methods::OrderDsl, + { + methods::OrderDsl::order(self, expr) + } + + /// Alias for `order` + fn order_by(self, expr: Expr) -> Order + where + Expr: Expression, + Self: methods::OrderDsl, + { + QueryDsl::order(self, expr) + } + + /// Appends to the `ORDER BY` clause of this SQL query. + /// + /// Unlike `.order`, this method will append rather than replace. + /// In other words, + /// `.order_by(foo).order_by(bar)` is equivalent to `.order_by(bar)`. + /// In contrast, + /// `.order_by(foo).then_order_by(bar)` is equivalent to `.order((foo, bar))`. + /// This method is only present on boxed queries. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// # connection.execute("DELETE FROM users")?; + /// diesel::insert_into(users) + /// .values(&vec![ + /// name.eq("Saul"), + /// name.eq("Steve"), + /// name.eq("Stan"), + /// name.eq("Stan"), + /// ]) + /// .execute(&connection)?; + /// + /// let data = users.select((name, id)) + /// .order_by(name.asc()) + /// .then_order_by(id.desc()) + /// .load(&connection)?; + /// let expected_data = vec![ + /// (String::from("Saul"), 3), + /// (String::from("Stan"), 6), + /// (String::from("Stan"), 5), + /// (String::from("Steve"), 4), + /// ]; + /// assert_eq!(expected_data, data); + /// # Ok(()) + /// # } + /// ``` + fn then_order_by(self, order: Order) -> ThenOrderBy + where + Self: methods::ThenOrderDsl, + { + methods::ThenOrderDsl::then_order_by(self, order) + } + + /// Sets the limit clause of the query. + /// + /// If there was already a limit clause, it will be overridden. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::users; + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use self::users::dsl::*; + /// # let connection = establish_connection(); + /// # diesel::delete(users).execute(&connection)?; + /// # diesel::insert_into(users) + /// # .values(&vec![ + /// # name.eq("Sean"), + /// # name.eq("Bastien"), + /// # name.eq("Pascal"), + /// # ]) + /// # .execute(&connection)?; + /// # + /// // Using a limit + /// let limited = users.select(name) + /// .order(id) + /// .limit(1) + /// .load::(&connection)?; + /// + /// // Without a limit + /// let no_limit = users.select(name) + /// .order(id) + /// .load::(&connection)?; + /// + /// assert_eq!(vec!["Sean"], limited); + /// assert_eq!(vec!["Sean", "Bastien", "Pascal"], no_limit); + /// # Ok(()) + /// # } + /// ``` + fn limit(self, limit: i64) -> Limit + where + Self: methods::LimitDsl, + { + methods::LimitDsl::limit(self, limit) + } + + /// Sets the offset clause of the query. + /// + /// If there was already a offset clause, it will be overridden. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::users; + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use self::users::dsl::*; + /// # let connection = establish_connection(); + /// # diesel::delete(users).execute(&connection)?; + /// # diesel::insert_into(users) + /// # .values(&vec![ + /// # name.eq("Sean"), + /// # name.eq("Bastien"), + /// # name.eq("Pascal"), + /// # ]) + /// # .execute(&connection)?; + /// # + /// // Using an offset + /// let offset = users.select(name) + /// .order(id) + /// .limit(2) + /// .offset(1) + /// .load::(&connection)?; + /// + /// // No Offset + /// let no_offset = users.select(name) + /// .order(id) + /// .limit(2) + /// .load::(&connection)?; + /// + /// assert_eq!(vec!["Bastien", "Pascal"], offset); + /// assert_eq!(vec!["Sean", "Bastien"], no_offset); + /// # Ok(()) + /// # } + /// ``` + fn offset(self, offset: i64) -> Offset + where + Self: methods::OffsetDsl, + { + methods::OffsetDsl::offset(self, offset) + } + + /// Sets the `group by` clause of a query. + /// + /// **Note:** Queries having a `group by` clause require a custom select clause. + /// Use `QueryDsl::select()` to specify one + /// + /// If there was already a group by clause, it will be overridden. + /// Ordering by multiple columns can be achieved by passing a tuple of those + /// columns. + /// + /// Diesel follows postgresql's group by semantic, this means any column + /// appearing in a group by clause is considered to be aggregated. If a + /// primary key is part of the group by clause every column from the + /// corresponding table is considerd to be aggregated. Select clauses + /// cannot mix aggregated and non aggregated expressions. + /// + /// For group by clauses containing columns from more than one table it + /// is required to call [`allow_columns_to_appear_in_same_group_by_clause!`] + /// + /// [`allow_columns_to_appear_in_same_group_by_clause!`]: ../macro.allow_columns_to_appear_in_same_group_by_clause.html + /// + /// # Examples + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use crate::schema::{users, posts}; + /// # use diesel::dsl::count; + /// # let connection = establish_connection(); + /// let data = users::table.inner_join(posts::table) + /// .group_by(users::id) + /// .select((users::name, count(posts::id))) + /// # .order_by(users::id.asc()) + /// .load::<(String, i64)>(&connection)?; + /// + /// assert_eq!(vec![(String::from("Sean"), 2), (String::from("Tess"), 1)], data); + /// # Ok(()) + /// # } + /// ``` + fn group_by(self, group_by: GB) -> GroupBy + where + GB: Expression, + Self: methods::GroupByDsl, + { + methods::GroupByDsl::group_by(self, group_by) + } + + /// Adds `FOR UPDATE` to the end of the select statement. + /// + /// This method is only available for MySQL and PostgreSQL. SQLite does not + /// provide any form of row locking. + /// + /// Additionally, `.for_update` cannot be used on queries with a distinct + /// clause, group by clause, having clause, or any unions. Queries with + /// a `FOR UPDATE` clause cannot be boxed. + /// + /// # Example + /// + /// ```ignore + /// // Executes `SELECT * FROM users FOR UPDATE` + /// users.for_update().load(&connection) + /// ``` + fn for_update(self) -> ForUpdate + where + Self: methods::LockingDsl, + { + methods::LockingDsl::with_lock(self, lock::ForUpdate) + } + + /// Adds `FOR NO KEY UPDATE` to the end of the select statement. + /// + /// This method is only available for PostgreSQL. SQLite does not + /// provide any form of row locking, and MySQL does not support anything + /// finer than row-level locking. + /// + /// Additionally, `.for_no_key_update` cannot be used on queries with a distinct + /// clause, group by clause, having clause, or any unions. Queries with + /// a `FOR NO KEY UPDATE` clause cannot be boxed. + /// + /// # Example + /// + /// ```ignore + /// // Executes `SELECT * FROM users FOR NO KEY UPDATE` + /// users.for_no_key_update().load(&connection) + /// ``` + fn for_no_key_update(self) -> ForNoKeyUpdate + where + Self: methods::LockingDsl, + { + methods::LockingDsl::with_lock(self, lock::ForNoKeyUpdate) + } + + /// Adds `FOR SHARE` to the end of the select statement. + /// + /// This method is only available for MySQL and PostgreSQL. SQLite does not + /// provide any form of row locking. + /// + /// Additionally, `.for_share` cannot be used on queries with a distinct + /// clause, group by clause, having clause, or any unions. Queries with + /// a `FOR SHARE` clause cannot be boxed. + /// + /// # Example + /// + /// ```ignore + /// // Executes `SELECT * FROM users FOR SHARE` + /// users.for_share().load(&connection) + /// ``` + fn for_share(self) -> ForShare + where + Self: methods::LockingDsl, + { + methods::LockingDsl::with_lock(self, lock::ForShare) + } + + /// Adds `FOR KEY SHARE` to the end of the select statement. + /// + /// This method is only available for PostgreSQL. SQLite does not + /// provide any form of row locking, and MySQL does not support anything + /// finer than row-level locking. + /// + /// Additionally, `.for_key_share` cannot be used on queries with a distinct + /// clause, group by clause, having clause, or any unions. Queries with + /// a `FOR KEY SHARE` clause cannot be boxed. + /// + /// # Example + /// + /// ```ignore + /// // Executes `SELECT * FROM users FOR KEY SHARE` + /// users.for_key_share().load(&connection) + /// ``` + fn for_key_share(self) -> ForKeyShare + where + Self: methods::LockingDsl, + { + methods::LockingDsl::with_lock(self, lock::ForKeyShare) + } + + /// Adds `SKIP LOCKED` to the end of a `FOR UPDATE` clause. + /// + /// This modifier is only supported in PostgreSQL 9.5+ and MySQL 8+. + /// + /// # Example + /// + /// ```ignore + /// // Executes `SELECT * FROM users FOR UPDATE SKIP LOCKED` + /// users.for_update().skip_locked().load(&connection) + /// ``` + fn skip_locked(self) -> SkipLocked + where + Self: methods::ModifyLockDsl, + { + methods::ModifyLockDsl::modify_lock(self, lock::SkipLocked) + } + + /// Adds `NOWAIT` to the end of a `FOR UPDATE` clause. + /// + /// This modifier is only supported in PostgreSQL 9.5+ and MySQL 8+. + /// + /// # Example + /// + /// ```ignore + /// // Executes `SELECT * FROM users FOR UPDATE NOWAIT` + /// users.for_update().no_wait().load(&connection) + /// ``` + fn no_wait(self) -> NoWait + where + Self: methods::ModifyLockDsl, + { + methods::ModifyLockDsl::modify_lock(self, lock::NoWait) + } + + /// Boxes the pieces of a query into a single type. + /// + /// This is useful for cases where you want to conditionally modify a query, + /// but need the type to remain the same. The backend must be specified as + /// part of this. It is not possible to box a query and have it be useable + /// on multiple backends. + /// + /// A boxed query will incur a minor performance penalty, as the query builder + /// can no longer be inlined by the compiler. For most applications this cost + /// will be minimal. + /// + /// ### Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::users; + /// # + /// # fn main() { + /// # use std::collections::HashMap; + /// # let connection = establish_connection(); + /// # let mut params = HashMap::new(); + /// # params.insert("name", "Sean"); + /// let mut query = users::table.into_boxed(); + /// if let Some(name) = params.get("name") { + /// query = query.filter(users::name.eq(name)); + /// } + /// let users = query.load(&connection); + /// # let expected = vec![(1, String::from("Sean"))]; + /// # assert_eq!(Ok(expected), users); + /// # } + /// ``` + /// + /// Diesel queries also have a similar problem to [`Iterator`][iterator], where + /// returning them from a function requires exposing the implementation of that + /// function. The [`helper_types`][helper_types] module exists to help with this, + /// but you might want to hide the return type or have it conditionally change. + /// Boxing can achieve both. + /// + /// [iterator]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html + /// [helper_types]: ../helper_types/index.html + /// + /// ### Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::users; + /// # + /// # fn main() { + /// # let connection = establish_connection(); + /// fn users_by_name(name: &str) -> users::BoxedQuery { + /// users::table.filter(users::name.eq(name)).into_boxed() + /// } + /// + /// assert_eq!(Ok(1), users_by_name("Sean").select(users::id).first(&connection)); + /// assert_eq!(Ok(2), users_by_name("Tess").select(users::id).first(&connection)); + /// # } + /// ``` + fn into_boxed<'a, DB>(self) -> IntoBoxed<'a, Self, DB> + where + DB: Backend, + Self: methods::BoxedDsl<'a, DB>, + { + methods::BoxedDsl::internal_into_boxed(self) + } + + /// Wraps this select statement in parenthesis, allowing it to be used + /// as an expression. + /// + /// SQL allows queries such as `foo = (SELECT ...)`, as long as the + /// subselect returns only a single column, and 0 or 1 rows. This method + /// indicates that you expect the query to only return a single value (this + /// will be enforced by adding `LIMIT 1`). + /// + /// The SQL type of this will always be `Nullable`, as the query returns + /// `NULL` if the table is empty or it otherwise returns 0 rows. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::insert_into; + /// # use schema::users::dsl::*; + /// # use schema::posts; + /// # let connection = establish_connection(); + /// insert_into(posts::table) + /// .values(posts::user_id.eq(1)) + /// .execute(&connection)?; + /// let last_post = posts::table + /// .order(posts::id.desc()); + /// let most_recently_active_user = users.select(name) + /// .filter(id.nullable().eq(last_post.select(posts::user_id).single_value())) + /// .first::(&connection)?; + /// assert_eq!("Sean", most_recently_active_user); + /// # Ok(()) + /// # } + /// ``` + fn single_value(self) -> SingleValue + where + Self: methods::SingleValueDsl, + { + methods::SingleValueDsl::single_value(self) + } + + /// Coerce the SQL type of the select clause to it's nullable equivalent. + /// + /// This is use full for writing queries that contain subselects on non null + /// fields comparing them to nullable fields. + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # let connection = establish_connection(); + /// table! { + /// users { + /// id -> Integer, + /// name -> Text, + /// } + /// } + /// + /// table! { + /// posts { + /// id -> Integer, + /// by_user -> Nullable, + /// } + /// } + /// + /// # let _: Vec<(i32, Option)> = + /// posts::table.filter( + /// posts::by_user.eq_any(users::table.select(users::name).nullable()) + /// ).load(&connection)?; + /// # Ok(()) + /// # } + fn nullable(self) -> NullableSelect + where + Self: methods::SelectNullableDsl, + { + methods::SelectNullableDsl::nullable(self) + } +} + +impl QueryDsl for T {} + +/// Methods used to execute queries. +pub trait RunQueryDsl: Sized { + /// Executes the given command, returning the number of rows affected. + /// + /// `execute` is usually used in conjunction with [`insert_into`](../fn.insert_into.html), + /// [`update`](../fn.update.html) and [`delete`](../fn.delete.html) where the number of + /// affected rows is often enough information. + /// + /// When asking the database to return data from a query, [`load`](#method.load) should + /// probably be used instead. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::insert_into; + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let inserted_rows = insert_into(users) + /// .values(name.eq("Ruby")) + /// .execute(&connection)?; + /// assert_eq!(1, inserted_rows); + /// + /// let inserted_rows = insert_into(users) + /// .values(&vec![name.eq("Jim"), name.eq("James")]) + /// .execute(&connection)?; + /// assert_eq!(2, inserted_rows); + /// # Ok(()) + /// # } + /// ``` + fn execute(self, conn: &Conn) -> QueryResult + where + Conn: Connection, + Self: methods::ExecuteDsl, + { + methods::ExecuteDsl::execute(self, conn) + } + + /// Executes the given query, returning a `Vec` with the returned rows. + /// + /// When using the query builder, + /// the return type can be + /// a tuple of the values, + /// or a struct which implements [`Queryable`]. + /// + /// When this method is called on [`sql_query`], + /// the return type can only be a struct which implements [`QueryableByName`] + /// + /// For insert, update, and delete operations where only a count of affected is needed, + /// [`execute`] should be used instead. + /// + /// [`Queryable`]: ../deserialize/trait.Queryable.html + /// [`QueryableByName`]: ../deserialize/trait.QueryableByName.html + /// [`execute`]: fn.execute.html + /// [`sql_query`]: ../fn.sql_query.html + /// + /// # Examples + /// + /// ## Returning a single field + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::insert_into; + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let data = users.select(name) + /// .load::(&connection)?; + /// assert_eq!(vec!["Sean", "Tess"], data); + /// # Ok(()) + /// # } + /// ``` + /// + /// ## Returning a tuple + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::insert_into; + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let data = users + /// .load::<(i32, String)>(&connection)?; + /// let expected_data = vec![ + /// (1, String::from("Sean")), + /// (2, String::from("Tess")), + /// ]; + /// assert_eq!(expected_data, data); + /// # Ok(()) + /// # } + /// ``` + /// + /// ## Returning a struct + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// #[derive(Queryable, PartialEq, Debug)] + /// struct User { + /// id: i32, + /// name: String, + /// } + /// + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::insert_into; + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let data = users + /// .load::(&connection)?; + /// let expected_data = vec![ + /// User { id: 1, name: String::from("Sean"), }, + /// User { id: 2, name: String::from("Tess"), }, + /// ]; + /// assert_eq!(expected_data, data); + /// # Ok(()) + /// # } + /// ``` + fn load(self, conn: &Conn) -> QueryResult> + where + Self: LoadQuery, + { + self.internal_load(conn) + } + + /// Runs the command, and returns the affected row. + /// + /// `Err(NotFound)` will be returned if the query affected 0 rows. You can + /// call `.optional()` on the result of this if the command was optional to + /// get back a `Result>` + /// + /// When this method is called on an insert, update, or delete statement, + /// it will implicitly add a `RETURNING *` to the query, + /// unless a returning clause was already specified. + /// + /// This method only returns the first row that was affected, even if more + /// rows are affected. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # #[cfg(feature = "postgres")] + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::{insert_into, update}; + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let inserted_row = insert_into(users) + /// .values(name.eq("Ruby")) + /// .get_result(&connection)?; + /// assert_eq!((3, String::from("Ruby")), inserted_row); + /// + /// // This will return `NotFound`, as there is no user with ID 4 + /// let update_result = update(users.find(4)) + /// .set(name.eq("Jim")) + /// .get_result::<(i32, String)>(&connection); + /// assert_eq!(Err(diesel::NotFound), update_result); + /// # Ok(()) + /// # } + /// # + /// # #[cfg(not(feature = "postgres"))] + /// # fn run_test() -> QueryResult<()> { + /// # Ok(()) + /// # } + /// ``` + fn get_result(self, conn: &Conn) -> QueryResult + where + Self: LoadQuery, + { + first_or_not_found(self.load(conn)) + } + + /// Runs the command, returning an `Vec` with the affected rows. + /// + /// This method is an alias for [`load`], but with a name that makes more + /// sense for insert, update, and delete statements. + /// + /// [`load`]: #method.load + fn get_results(self, conn: &Conn) -> QueryResult> + where + Self: LoadQuery, + { + self.load(conn) + } + + /// Attempts to load a single record. + /// + /// This method is equivalent to `.limit(1).get_result()` + /// + /// Returns `Ok(record)` if found, and `Err(NotFound)` if no results are + /// returned. If the query truly is optional, you can call `.optional()` on + /// the result of this to get a `Result>`. + /// + /// # Example: + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// diesel::insert_into(users) + /// .values(&vec![name.eq("Sean"), name.eq("Pascal")]) + /// .execute(&connection)?; + /// + /// let first_name = users.order(id).select(name).first(&connection); + /// assert_eq!(Ok(String::from("Sean")), first_name); + /// + /// let not_found = users + /// .filter(name.eq("Foo")) + /// .first::<(i32, String)>(&connection); + /// assert_eq!(Err(diesel::NotFound), not_found); + /// # Ok(()) + /// # } + /// ``` + fn first(self, conn: &Conn) -> QueryResult + where + Self: methods::LimitDsl, + Limit: LoadQuery, + { + methods::LimitDsl::limit(self, 1).get_result(conn) + } +} + +// Note: We could have a blanket `AsQuery` impl here, which would apply to +// everything we want it to. However, when a query is invalid, we specifically +// want the error to happen on the where clause of the method instead of trait +// resolution. Otherwise our users will get an error saying `<3 page long type>: +// ExecuteDsl is not satisfied` instead of a specific error telling them what +// part of their query is wrong. +impl RunQueryDsl for T where T: Table {} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/nullable_select_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/nullable_select_dsl.rs new file mode 100644 index 000000000..be8e4d413 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/nullable_select_dsl.rs @@ -0,0 +1,14 @@ +/// The `nullable` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However you may need a where clause on this trait +/// to call `nullable` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html#method.nullable +pub trait SelectNullableDsl { + /// The return type of `nullable` + type Output; + + /// See the trait documentation + fn nullable(self) -> Self::Output; +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/offset_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/offset_dsl.rs new file mode 100644 index 000000000..05f063651 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/offset_dsl.rs @@ -0,0 +1,28 @@ +use crate::query_source::Table; + +/// The `offset` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `offset` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait OffsetDsl { + /// The type returned by `.offset`. + type Output; + + /// See the trait documentation + fn offset(self, offset: i64) -> Self::Output; +} + +impl OffsetDsl for T +where + T: Table, + T::Query: OffsetDsl, +{ + type Output = ::Output; + + fn offset(self, offset: i64) -> Self::Output { + self.as_query().offset(offset) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/order_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/order_dsl.rs new file mode 100644 index 000000000..116e69cd9 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/order_dsl.rs @@ -0,0 +1,58 @@ +use crate::expression::Expression; +use crate::query_source::Table; + +/// The `order` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `order` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait OrderDsl { + /// The type returned by `.order`. + type Output; + + /// See the trait documentation. + fn order(self, expr: Expr) -> Self::Output; +} + +impl OrderDsl for T +where + Expr: Expression, + T: Table, + T::Query: OrderDsl, +{ + type Output = >::Output; + + fn order(self, expr: Expr) -> Self::Output { + self.as_query().order(expr) + } +} + +/// The `then_order_by` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `then_order_by` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait ThenOrderDsl { + /// The type returned by `.then_order_by`. + type Output; + + /// See the trait documentation. + fn then_order_by(self, expr: Expr) -> Self::Output; +} + +impl ThenOrderDsl for T +where + Expr: Expression, + T: Table, + T::Query: ThenOrderDsl, +{ + type Output = >::Output; + + fn then_order_by(self, expr: Expr) -> Self::Output { + self.as_query().then_order_by(expr) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/save_changes_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/save_changes_dsl.rs new file mode 100644 index 000000000..896e8b78f --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/save_changes_dsl.rs @@ -0,0 +1,155 @@ +use crate::associations::HasTable; +#[cfg(any(feature = "sqlite", feature = "mysql"))] +use crate::associations::Identifiable; +use crate::connection::Connection; +#[cfg(any(feature = "sqlite", feature = "mysql"))] +use crate::dsl::Find; +#[cfg(any(feature = "sqlite", feature = "postgres", feature = "mysql"))] +use crate::dsl::Update; +#[cfg(any(feature = "sqlite", feature = "postgres", feature = "mysql"))] +use crate::expression::{is_aggregate, MixedAggregates, ValidGrouping}; +use crate::query_builder::{AsChangeset, IntoUpdateTarget}; +#[cfg(any(feature = "sqlite", feature = "mysql"))] +use crate::query_dsl::methods::{ExecuteDsl, FindDsl}; +#[cfg(any(feature = "sqlite", feature = "postgres", feature = "mysql"))] +use crate::query_dsl::{LoadQuery, RunQueryDsl}; +use crate::result::QueryResult; +#[cfg(any(feature = "sqlite", feature = "postgres", feature = "mysql"))] +use crate::Table; + +/// A trait defining how to update a record and fetch the updated entry +/// on a certain backend. +/// +/// The only case where it is required to work with this trait is while +/// implementing a new connection type. +/// Otherwise use [`SaveChangesDsl`](trait.SaveChangesDsl.html) +/// +/// For implementing this trait for a custom backend: +/// * The `Changes` generic parameter represents the changeset that should be stored +/// * The `Output` generic parameter represents the type of the response. +pub trait UpdateAndFetchResults: Connection { + /// See the traits documentation. + fn update_and_fetch(&self, changeset: Changes) -> QueryResult; +} + +#[cfg(feature = "postgres")] +use crate::pg::PgConnection; + +#[cfg(feature = "postgres")] +impl UpdateAndFetchResults for PgConnection +where + Changes: Copy + AsChangeset::Table> + IntoUpdateTarget, + Update: LoadQuery, + ::AllColumns: ValidGrouping<()>, + <::AllColumns as ValidGrouping<()>>::IsAggregate: + MixedAggregates, +{ + fn update_and_fetch(&self, changeset: Changes) -> QueryResult { + crate::update(changeset).set(changeset).get_result(self) + } +} + +#[cfg(feature = "sqlite")] +use crate::sqlite::SqliteConnection; + +#[cfg(feature = "sqlite")] +impl UpdateAndFetchResults for SqliteConnection +where + Changes: Copy + Identifiable, + Changes: AsChangeset::Table> + IntoUpdateTarget, + Changes::Table: FindDsl, + Update: ExecuteDsl, + Find: LoadQuery, + ::AllColumns: ValidGrouping<()>, + <::AllColumns as ValidGrouping<()>>::IsAggregate: + MixedAggregates, +{ + fn update_and_fetch(&self, changeset: Changes) -> QueryResult { + crate::update(changeset).set(changeset).execute(self)?; + Changes::table().find(changeset.id()).get_result(self) + } +} + +#[cfg(feature = "mysql")] +use crate::mysql::MysqlConnection; + +#[cfg(feature = "mysql")] +impl UpdateAndFetchResults for MysqlConnection +where + Changes: Copy + Identifiable, + Changes: AsChangeset::Table> + IntoUpdateTarget, + Changes::Table: FindDsl, + Update: ExecuteDsl, + Find: LoadQuery, + ::AllColumns: ValidGrouping<()>, + <::AllColumns as ValidGrouping<()>>::IsAggregate: + MixedAggregates, +{ + fn update_and_fetch(&self, changeset: Changes) -> QueryResult { + crate::update(changeset).set(changeset).execute(self)?; + Changes::table().find(changeset.id()).get_result(self) + } +} + +/// Sugar for types which implement both `AsChangeset` and `Identifiable` +/// +/// On backends which support the `RETURNING` keyword, +/// `foo.save_changes(&conn)` is equivalent to +/// `update(&foo).set(&foo).get_result(&conn)`. +/// On other backends, two queries will be executed. +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use schema::animals; +/// # +/// #[derive(Queryable, Debug, PartialEq)] +/// struct Animal { +/// id: i32, +/// species: String, +/// legs: i32, +/// name: Option, +/// } +/// +/// #[derive(AsChangeset, Identifiable)] +/// #[table_name = "animals"] +/// struct AnimalForm<'a> { +/// id: i32, +/// name: &'a str, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use self::animals::dsl::*; +/// # let connection = establish_connection(); +/// let form = AnimalForm { id: 2, name: "Super scary" }; +/// let changed_animal = form.save_changes(&connection)?; +/// let expected_animal = Animal { +/// id: 2, +/// species: String::from("spider"), +/// legs: 8, +/// name: Some(String::from("Super scary")), +/// }; +/// assert_eq!(expected_animal, changed_animal); +/// # Ok(()) +/// # } +/// ``` +pub trait SaveChangesDsl { + /// See the trait documentation. + fn save_changes(self, connection: &Conn) -> QueryResult + where + Self: Sized, + Conn: UpdateAndFetchResults, + { + connection.update_and_fetch(self) + } +} + +impl SaveChangesDsl for T where + T: Copy + AsChangeset::Table> + IntoUpdateTarget +{ +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/select_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/select_dsl.rs new file mode 100644 index 000000000..2385f6976 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/select_dsl.rs @@ -0,0 +1,33 @@ +use crate::expression::Expression; +use crate::query_source::Table; + +/// The `select` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `select` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait SelectDsl { + // FIXME: Once we've refactored the `impl Expression` on `SelectStatement` + // to not conditionally be `sql_types::Array`, it is probably worthwhile to + // add a `: Expression` bound here. + /// The type returned by `.select` + type Output; + + /// See the trait documentation + fn select(self, selection: Selection) -> Self::Output; +} + +impl SelectDsl for T +where + Selection: Expression, + T: Table, + T::Query: SelectDsl, +{ + type Output = >::Output; + + fn select(self, selection: Selection) -> Self::Output { + self.as_query().select(selection) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/single_value_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/single_value_dsl.rs new file mode 100644 index 000000000..c220b6a93 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/single_value_dsl.rs @@ -0,0 +1,34 @@ +use super::methods::LimitDsl; +use crate::dsl::Limit; +use crate::expression::grouped::Grouped; +use crate::expression::subselect::Subselect; +use crate::query_builder::SelectQuery; +use crate::sql_types::IntoNullable; + +/// The `single_value` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `single_value` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait SingleValueDsl { + /// The type returned by `.single_value`. + type Output; + + /// See the trait documentation. + fn single_value(self) -> Self::Output; +} + +impl SingleValueDsl for T +where + Self: SelectQuery + LimitDsl, + ::SqlType: IntoNullable, +{ + type Output = + Grouped, <::SqlType as IntoNullable>::Nullable>>; + + fn single_value(self) -> Self::Output { + Grouped(Subselect::new(self.limit(1))) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_source/joins.rs b/collector/benchmarks/diesel/diesel/src/query_source/joins.rs new file mode 100644 index 000000000..76461fe4a --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_source/joins.rs @@ -0,0 +1,341 @@ +use super::{AppearsInFromClause, Plus, QuerySource}; +use crate::backend::Backend; +use crate::expression::grouped::Grouped; +use crate::expression::nullable::Nullable; +use crate::expression::SelectableExpression; +use crate::prelude::*; +use crate::query_builder::*; +use crate::query_dsl::InternalJoinDsl; +use crate::result::QueryResult; +use crate::sql_types::BoolOrNullableBool; +use crate::util::TupleAppend; + +#[derive(Debug, Clone, Copy, QueryId)] +/// A query source representing the join between two tables +pub struct Join { + left: Left, + right: Right, + kind: Kind, +} + +#[derive(Debug, Clone, Copy, QueryId)] +#[doc(hidden)] +/// A query source representing the join between two tables with an explicit +/// `ON` given. `Join` should usually be referenced instead, as all "type +/// safety" traits are implemented in terms of `Join` implementing them. +pub struct JoinOn { + join: Join, + on: On, +} + +impl Join { + pub fn new(left: Left, right: Right, kind: Kind) -> Self { + Join { + left: left, + right: right, + kind: kind, + } + } + + #[doc(hidden)] + pub fn on(self, on: On) -> JoinOn { + JoinOn { join: self, on: on } + } +} + +impl QuerySource for Join +where + Left: QuerySource + AppendSelection, + Right: QuerySource, + Left::Output: SelectableExpression, + Self: Clone, +{ + type FromClause = Self; + type DefaultSelection = Left::Output; + + fn from_clause(&self) -> Self::FromClause { + self.clone() + } + + fn default_selection(&self) -> Self::DefaultSelection { + self.left.append_selection(self.right.default_selection()) + } +} + +impl QuerySource for Join +where + Left: QuerySource + AppendSelection>, + Right: QuerySource, + Left::Output: SelectableExpression, + Self: Clone, +{ + type FromClause = Self; + type DefaultSelection = Left::Output; + + fn from_clause(&self) -> Self::FromClause { + self.clone() + } + + fn default_selection(&self) -> Self::DefaultSelection { + self.left + .append_selection(self.right.default_selection().nullable()) + } +} + +impl QuerySource for JoinOn +where + Join: QuerySource, + On: AppearsOnTable + Clone, + On::SqlType: BoolOrNullableBool, + Join::DefaultSelection: SelectableExpression, +{ + type FromClause = Grouped>; + type DefaultSelection = Join::DefaultSelection; + + fn from_clause(&self) -> Self::FromClause { + Grouped(nodes::InfixNode::new( + self.join.from_clause(), + self.on.clone(), + " ON ", + )) + } + + fn default_selection(&self) -> Self::DefaultSelection { + self.join.default_selection() + } +} + +impl QueryFragment for Join +where + DB: Backend, + Left: QuerySource, + Left::FromClause: QueryFragment, + Right: QuerySource, + Right::FromClause: QueryFragment, + Kind: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.left.from_clause().walk_ast(out.reborrow())?; + self.kind.walk_ast(out.reborrow())?; + out.push_sql(" JOIN "); + self.right.from_clause().walk_ast(out.reborrow())?; + Ok(()) + } +} + +/// Indicates that two tables can be joined without an explicit `ON` clause. +/// +/// Implementations of this trait are generated by invoking [`joinable!`]. +/// Implementing this trait means that you can call +/// `left_table.inner_join(right_table)`, without supplying the `ON` clause +/// explicitly. To join two tables which do not implement this trait, you will +/// need to call [`.on`]. +/// +/// See [`joinable!`] and [`inner_join`] for usage examples. +/// +/// [`joinable!`]: ../macro.joinable.html +/// [`.on`]: ../query_dsl/trait.JoinOnDsl.html#method.on +/// [`inner_join`]: ../query_dsl/trait.QueryDsl.html#method.inner_join +pub trait JoinTo { + #[doc(hidden)] + type FromClause; + #[doc(hidden)] + type OnClause; + #[doc(hidden)] + fn join_target(rhs: T) -> (Self::FromClause, Self::OnClause); +} + +#[doc(hidden)] +/// Used to ensure the sql type of `left.join(mid).join(right)` is +/// `(Left, Mid, Right)` and not `((Left, Mid), Right)`. This needs +/// to be separate from `TupleAppend` because we still want to keep +/// the column lists (which are tuples) separate. +pub trait AppendSelection { + type Output; + + fn append_selection(&self, selection: Selection) -> Self::Output; +} + +impl AppendSelection for T { + type Output = (T::AllColumns, Selection); + + fn append_selection(&self, selection: Selection) -> Self::Output { + (T::all_columns(), selection) + } +} + +impl AppendSelection for Join +where + Self: QuerySource, + ::DefaultSelection: TupleAppend, +{ + type Output = <::DefaultSelection as TupleAppend>::Output; + + fn append_selection(&self, selection: Selection) -> Self::Output { + self.default_selection().tuple_append(selection) + } +} + +impl AppendSelection for JoinOn +where + Join: AppendSelection, +{ + type Output = Join::Output; + + fn append_selection(&self, selection: Selection) -> Self::Output { + self.join.append_selection(selection) + } +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy, Default, QueryId)] +pub struct Inner; + +impl QueryFragment for Inner { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" INNER"); + Ok(()) + } +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy, Default, QueryId)] +pub struct LeftOuter; + +impl QueryFragment for LeftOuter { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" LEFT OUTER"); + Ok(()) + } +} + +impl JoinTo for Join +where + Left: JoinTo, +{ + type FromClause = Left::FromClause; + type OnClause = Left::OnClause; + + fn join_target(rhs: Right) -> (Self::FromClause, Self::OnClause) { + Left::join_target(rhs) + } +} + +impl JoinTo for JoinOn +where + Join: JoinTo, +{ + type FromClause = Join::FromClause; + type OnClause = Join::OnClause; + + fn join_target(rhs: Right) -> (Self::FromClause, Self::OnClause) { + Join::join_target(rhs) + } +} + +impl AppearsInFromClause for Join +where + Left: AppearsInFromClause, + Right: AppearsInFromClause, + Left::Count: Plus, +{ + type Count = >::Output; +} + +impl AppearsInFromClause for JoinOn +where + Join: AppearsInFromClause, +{ + type Count = Join::Count; +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct OnClauseWrapper { + source: Source, + on: On, +} + +impl OnClauseWrapper { + pub fn new(source: Source, on: On) -> Self { + OnClauseWrapper { source, on } + } +} + +impl JoinTo> for Lhs +where + Lhs: Table, +{ + type FromClause = Rhs; + type OnClause = On; + + fn join_target(rhs: OnClauseWrapper) -> (Self::FromClause, Self::OnClause) { + (rhs.source, rhs.on) + } +} + +impl JoinTo for OnClauseWrapper +where + Lhs: JoinTo, +{ + type FromClause = >::FromClause; + type OnClause = >::OnClause; + + fn join_target(rhs: Rhs) -> (Self::FromClause, Self::OnClause) { + >::join_target(rhs) + } +} + +impl InternalJoinDsl for OnClauseWrapper +where + Lhs: InternalJoinDsl, +{ + type Output = OnClauseWrapper<>::Output, On2>; + + fn join(self, rhs: Rhs, kind: Kind, on: On1) -> Self::Output { + OnClauseWrapper { + source: self.source.join(rhs, kind, on), + on: self.on, + } + } +} + +impl QueryDsl for OnClauseWrapper {} + +#[doc(hidden)] +/// Convert any joins in a `FROM` clause into an inner join. +/// +/// This trait is used to determine whether +/// `Nullable: SelectableExpression`. We consider it to be +/// selectable if `T: SelectableExpression`. Since `SomeJoin` +/// may be deeply nested, we need to recursively change any appearances of +/// `LeftOuter` to `Inner` in order to perform this check. +pub trait ToInnerJoin { + type InnerJoin; +} + +impl ToInnerJoin for Join +where + Left: ToInnerJoin, + Right: ToInnerJoin, +{ + type InnerJoin = Join; +} + +impl ToInnerJoin for JoinOn +where + Join: ToInnerJoin, +{ + type InnerJoin = JoinOn; +} + +impl ToInnerJoin for SelectStatement +where + From: ToInnerJoin, +{ + type InnerJoin = SelectStatement; +} + +impl ToInnerJoin for T { + type InnerJoin = T; +} diff --git a/collector/benchmarks/diesel/diesel/src/query_source/mod.rs b/collector/benchmarks/diesel/diesel/src/query_source/mod.rs new file mode 100644 index 000000000..42b04316b --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_source/mod.rs @@ -0,0 +1,158 @@ +//! Types related to describing schema, and interactions between tables. +//! +//! Most traits in this module are derived or generated by [`table!`]. +//! +//! [`table!`]: ../macro.table.html +#[doc(hidden)] +pub mod joins; +mod peano_numbers; + +use crate::expression::{Expression, SelectableExpression, ValidGrouping}; +use crate::query_builder::*; + +pub use self::joins::JoinTo; +pub use self::peano_numbers::*; + +/// Represents a type which can appear in the `FROM` clause. Apps should not +/// need to concern themselves with this trait. +/// +/// Types which implement this trait include: +/// - Tables generated by the `table!` macro +/// - Internal structs which represent joins +/// - A select statement which has had no query builder methods called on it, +/// other than those which can affect the from clause. +pub trait QuerySource { + /// The type returned by `from_clause` + type FromClause; + /// The type returned by `default_selection` + type DefaultSelection: SelectableExpression; + + /// The actual `FROM` clause of this type. This is typically only called in + /// `QueryFragment` implementations. + fn from_clause(&self) -> Self::FromClause; + /// The default select clause of this type, which should be used if no + /// select clause was explicitly specified. This should always be a tuple of + /// all the desired columns, not `star` + fn default_selection(&self) -> Self::DefaultSelection; +} + +/// A column on a database table. Types which implement this trait should have +/// been generated by the [`table!` macro](../macro.table.html). +pub trait Column: Expression { + /// The table which this column belongs to + type Table: Table; + + /// The name of this column + const NAME: &'static str; +} + +/// A SQL database table. Types which implement this trait should have been +/// generated by the [`table!` macro](../macro.table.html). +pub trait Table: QuerySource + AsQuery + Sized { + /// The type returned by `primary_key` + type PrimaryKey: SelectableExpression + ValidGrouping<()>; + /// The type returned by `all_columns` + type AllColumns: SelectableExpression + ValidGrouping<()>; + + /// Returns the primary key of this table. + /// + /// If the table has a composite primary key, this will be a tuple. + fn primary_key(&self) -> Self::PrimaryKey; + /// Returns a tuple of all columns belonging to this table. + fn all_columns() -> Self::AllColumns; +} + +/// Determines how many times `Self` appears in `QS` +/// +/// This trait is primarily used to determine whether or not a column is +/// selectable from a given from clause. A column can be selected if its table +/// appears in the from clause *exactly once*. +/// +/// We do not allow the same table to appear in a query multiple times in any +/// context where referencing that table would be ambiguous (depending on the +/// context and backend being used, this may or may not be something that would +/// otherwise result in a runtime error). +pub trait AppearsInFromClause { + /// How many times does `Self` appear in `QS`? + type Count; +} + +#[doc(hidden)] +/// Used to determine which of two from clauses contains a given table. +/// +/// This trait can be used to emulate "or" conditions in where clauses when +/// we want a trait to be implemented with one of two type parameters. +/// +/// For example, if we wanted to write: +/// +/// ```rust,ignore +/// where +/// T: SelectableExpression | SelectableExpression, +/// ``` +/// +/// we can emulate this by writing: +/// +/// ```rust,ignore +/// where +/// Left: AppearsInFromClause, +/// Right: AppearsInFromClause, +/// (Left::Count, Right::Count): Pick, +/// T: SelectableExpression< +/// <(Left::Count, Right::Count) as Pick>::Selection, +/// >, +/// ``` +/// +/// In order to aquire the counts in the first place, we must already know +/// the table we're searching for. +pub trait Pick { + /// The selected type. + /// + /// For `(Once, Never)` this type will be `Left`. For `(Never, Once)`, this type will be + /// `Right` + type Selection; +} + +impl Pick for (Once, Never) { + type Selection = Left; +} + +impl Pick for (Never, Once) { + type Selection = Right; +} + +#[doc(hidden)] +#[allow( + non_camel_case_types, + missing_debug_implementations, + missing_copy_implementations +)] +/// Everything in this module is here to give something more helpful than: +/// +/// > (Never, Never): Pick is not satisifed +/// +/// Any of these impls can be deleted if they are getting in the way of +/// other functionality. Any code which is using these impls is already +/// failing to compile. +mod impls_which_are_only_here_to_improve_error_messages { + use super::*; + + pub struct this_table_doesnt_appear_in_the_from_clause_of_your_query; + + impl Pick for (Never, Never) { + type Selection = this_table_doesnt_appear_in_the_from_clause_of_your_query; + } + + pub struct this_table_appears_in_your_query_more_than_once_and_must_be_aliased; + + impl Pick for (MoreThanOnce, OtherCount) { + type Selection = this_table_appears_in_your_query_more_than_once_and_must_be_aliased; + } + + impl Pick for (Never, MoreThanOnce) { + type Selection = this_table_appears_in_your_query_more_than_once_and_must_be_aliased; + } + + impl Pick for (Once, MoreThanOnce) { + type Selection = this_table_appears_in_your_query_more_than_once_and_must_be_aliased; + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_source/peano_numbers.rs b/collector/benchmarks/diesel/diesel/src/query_source/peano_numbers.rs new file mode 100644 index 000000000..0b9cd0686 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_source/peano_numbers.rs @@ -0,0 +1,45 @@ +//! A simple implementation of peano numbers. +//! +//! This is used to enforce that columns can only be selected from a given from +//! clause if their table appears exactly one time. + +/// A table never appears in the from clause. +#[allow(missing_debug_implementations, missing_copy_implementations)] +pub struct Never; + +/// A table appears in the from clause exactly one time. +#[allow(missing_debug_implementations, missing_copy_implementations)] +pub struct Once; + +/// A table appears in the from clause two or more times. +#[allow(missing_debug_implementations, missing_copy_implementations)] +pub struct MoreThanOnce; + +/// Add two peano numbers together. +/// +/// This is used to determine the number of times a table appears in a from +/// clause when the from clause contains a join. +pub trait Plus { + /// The result of adding these numbers together + type Output; +} + +impl Plus for Never { + type Output = T; +} + +impl Plus for MoreThanOnce { + type Output = Self; +} + +impl Plus for Once { + type Output = Self; +} + +impl Plus for Once { + type Output = MoreThanOnce; +} + +impl Plus for Once { + type Output = MoreThanOnce; +} diff --git a/collector/benchmarks/diesel/diesel/src/r2d2.rs b/collector/benchmarks/diesel/diesel/src/r2d2.rs new file mode 100644 index 000000000..93863c3df --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/r2d2.rs @@ -0,0 +1,303 @@ +//! Connection pooling via r2d2. +//! +//! Note: This module requires enabling the `r2d2` feature + +extern crate r2d2; + +pub use self::r2d2::*; + +/// A re-export of [`r2d2::Error`], which is only used by methods on [`r2d2::Pool`]. +/// +/// [`r2d2::Error`]: ../../r2d2/struct.Error.html +/// [`r2d2::Pool`]: ../../r2d2/struct.Pool.html +pub type PoolError = self::r2d2::Error; + +use std::convert::Into; +use std::fmt; +use std::marker::PhantomData; + +use crate::connection::{SimpleConnection, TransactionManager}; +use crate::deserialize::FromSqlRow; +use crate::expression::QueryMetadata; +use crate::prelude::*; +use crate::query_builder::{AsQuery, QueryFragment, QueryId}; + +/// An r2d2 connection manager for use with Diesel. +/// +/// See the [r2d2 documentation] for usage examples. +/// +/// [r2d2 documentation]: ../../r2d2 +#[derive(Clone)] +pub struct ConnectionManager { + database_url: String, + _marker: PhantomData, +} + +impl fmt::Debug for ConnectionManager { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ConnectionManager<{}>", std::any::type_name::()) + } +} + +unsafe impl Sync for ConnectionManager {} + +impl ConnectionManager { + /// Returns a new connection manager, + /// which establishes connections to the given database URL. + pub fn new>(database_url: S) -> Self { + ConnectionManager { + database_url: database_url.into(), + _marker: PhantomData, + } + } +} + +/// The error used when managing connections with `r2d2`. +#[derive(Debug)] +pub enum Error { + /// An error occurred establishing the connection + ConnectionError(ConnectionError), + + /// An error occurred pinging the database + QueryError(crate::result::Error), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::ConnectionError(ref e) => e.fmt(f), + Error::QueryError(ref e) => e.fmt(f), + } + } +} + +impl ::std::error::Error for Error {} + +/// A trait indicating a connection could be used inside a r2d2 pool +pub trait R2D2Connection: Connection { + /// Check if a connection is still valid + fn ping(&self) -> QueryResult<()>; +} + +#[cfg(feature = "postgres")] +impl R2D2Connection for crate::pg::PgConnection { + fn ping(&self) -> QueryResult<()> { + self.execute("SELECT 1").map(|_| ()) + } +} + +#[cfg(feature = "mysql")] +impl R2D2Connection for crate::mysql::MysqlConnection { + fn ping(&self) -> QueryResult<()> { + self.execute("SELECT 1").map(|_| ()) + } +} + +#[cfg(feature = "sqlite")] +impl R2D2Connection for crate::sqlite::SqliteConnection { + fn ping(&self) -> QueryResult<()> { + self.execute("SELECT 1").map(|_| ()) + } +} + +impl ManageConnection for ConnectionManager +where + T: R2D2Connection + Send + 'static, +{ + type Connection = T; + type Error = Error; + + fn connect(&self) -> Result { + T::establish(&self.database_url).map_err(Error::ConnectionError) + } + + fn is_valid(&self, conn: &mut T) -> Result<(), Error> { + conn.ping().map_err(Error::QueryError) + } + + fn has_broken(&self, _conn: &mut T) -> bool { + false + } +} + +impl SimpleConnection for PooledConnection +where + M: ManageConnection, + M::Connection: R2D2Connection + Send + 'static, +{ + fn batch_execute(&self, query: &str) -> QueryResult<()> { + (&**self).batch_execute(query) + } +} + +impl Connection for PooledConnection +where + M: ManageConnection, + M::Connection: Connection + R2D2Connection + Send + 'static, +{ + type Backend = ::Backend; + type TransactionManager = PooledConnectionTransactionManager; + + fn establish(_: &str) -> ConnectionResult { + Err(ConnectionError::BadConnection(String::from( + "Cannot directly establish a pooled connection", + ))) + } + + fn execute(&self, query: &str) -> QueryResult { + (&**self).execute(query) + } + + fn load(&self, source: T) -> QueryResult> + where + T: AsQuery, + T::Query: QueryFragment + QueryId, + U: FromSqlRow, + Self::Backend: QueryMetadata, + { + (&**self).load(source) + } + + fn execute_returning_count(&self, source: &T) -> QueryResult + where + T: QueryFragment + QueryId, + { + (&**self).execute_returning_count(source) + } + + fn transaction_manager(&self) -> &Self::TransactionManager { + // This is actually fine because we have an #[repr(transparent)] + // on LoggingTransactionManager, which means the layout is the same + // as the inner type + // See the ref-cast crate for a longer version: https://github.com/dtolnay/ref-cast + unsafe { + &*((&**self).transaction_manager() as *const _ as *const Self::TransactionManager) + } + } +} + +#[doc(hidden)] +#[repr(transparent)] +#[allow(missing_debug_implementations)] +pub struct PooledConnectionTransactionManager +where + M: ManageConnection, + M::Connection: Connection, +{ + inner: ::TransactionManager, +} + +impl TransactionManager> for PooledConnectionTransactionManager +where + M: ManageConnection, + PooledConnection: Connection, + M::Connection: Connection, +{ + fn begin_transaction(&self, conn: &PooledConnection) -> QueryResult<()> { + self.inner.begin_transaction(&**conn) + } + + fn rollback_transaction(&self, conn: &PooledConnection) -> QueryResult<()> { + self.inner.rollback_transaction(&**conn) + } + + fn commit_transaction(&self, conn: &PooledConnection) -> QueryResult<()> { + self.inner.commit_transaction(&**conn) + } + + fn get_transaction_depth(&self) -> u32 { + self.inner.get_transaction_depth() + } +} + +impl crate::migration::MigrationConnection for PooledConnection +where + M: ManageConnection, + M::Connection: crate::migration::MigrationConnection, + Self: Connection, +{ + fn setup(&self) -> QueryResult { + (&**self).setup() + } +} + +impl crate::query_dsl::UpdateAndFetchResults + for PooledConnection +where + M: ManageConnection, + M::Connection: crate::query_dsl::UpdateAndFetchResults, + Self: Connection, +{ + fn update_and_fetch(&self, changeset: Changes) -> QueryResult { + (&**self).update_and_fetch(changeset) + } +} + +#[cfg(test)] +mod tests { + use std::sync::mpsc; + use std::sync::Arc; + use std::thread; + + use crate::r2d2::*; + use crate::test_helpers::*; + + #[test] + fn establish_basic_connection() { + let manager = ConnectionManager::::new(database_url()); + let pool = Arc::new(Pool::builder().max_size(2).build(manager).unwrap()); + + let (s1, r1) = mpsc::channel(); + let (s2, r2) = mpsc::channel(); + + let pool1 = Arc::clone(&pool); + let t1 = thread::spawn(move || { + let conn = pool1.get().unwrap(); + s1.send(()).unwrap(); + r2.recv().unwrap(); + drop(conn); + }); + + let pool2 = Arc::clone(&pool); + let t2 = thread::spawn(move || { + let conn = pool2.get().unwrap(); + s2.send(()).unwrap(); + r1.recv().unwrap(); + drop(conn); + }); + + t1.join().unwrap(); + t2.join().unwrap(); + + pool.get().unwrap(); + } + + #[test] + fn is_valid() { + let manager = ConnectionManager::::new(database_url()); + let pool = Pool::builder() + .max_size(1) + .test_on_check_out(true) + .build(manager) + .unwrap(); + + pool.get().unwrap(); + } + + #[test] + fn pooled_connection_impls_connection() { + use crate::select; + use crate::sql_types::Text; + + let manager = ConnectionManager::::new(database_url()); + let pool = Pool::builder() + .max_size(1) + .test_on_check_out(true) + .build(manager) + .unwrap(); + let conn = pool.get().unwrap(); + + let query = select("foo".into_sql::()); + assert_eq!("foo", query.get_result::(&conn).unwrap()); + } +} diff --git a/collector/benchmarks/diesel/diesel/src/result.rs b/collector/benchmarks/diesel/diesel/src/result.rs new file mode 100644 index 000000000..38b458f3c --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/result.rs @@ -0,0 +1,369 @@ +//! Errors, type aliases, and functions related to working with `Result`. + +use std::convert::From; +use std::error::Error as StdError; +use std::ffi::NulError; +use std::fmt::{self, Display}; + +#[derive(Debug)] +#[allow(clippy::enum_variant_names)] +/// Represents all the ways that a query can fail. +/// +/// This type is not intended to be exhaustively matched, and new variants may +/// be added in the future without a major version bump. +#[non_exhaustive] +pub enum Error { + /// The query contained a nul byte. + /// + /// This should never occur in normal usage. + InvalidCString(NulError), + + /// The database returned an error. + /// + /// While Diesel prevents almost all sources of runtime errors at compile + /// time, it does not attempt to prevent 100% of them. Typically this error + /// will occur from insert or update statements due to a constraint + /// violation. + DatabaseError( + DatabaseErrorKind, + Box, + ), + + /// No rows were returned by a query expected to return at least one row. + /// + /// This variant is only returned by [`get_result`] and [`first`]. [`load`] + /// does not treat 0 rows as an error. If you would like to allow either 0 + /// or 1 rows, call [`optional`] on the result. + /// + /// [`get_result`]: ../query_dsl/trait.RunQueryDsl.html#method.get_result + /// [`first`]: ../query_dsl/trait.RunQueryDsl.html#method.first + /// [`load`]: ../query_dsl/trait.RunQueryDsl.html#method.load + /// [`optional`]: trait.OptionalExtension.html#tymethod.optional + NotFound, + + /// The query could not be constructed + /// + /// An example of when this error could occur is if you are attempting to + /// construct an update statement with no changes (e.g. all fields on the + /// struct are `None`). + QueryBuilderError(Box), + + /// An error occurred deserializing the data being sent to the database. + /// + /// Typically this error means that the stated type of the query is + /// incorrect. An example of when this error might occur in normal usage is + /// attempting to deserialize an infinite date into chrono. + DeserializationError(Box), + + /// An error occurred serializing the data being sent to the database. + /// + /// An example of when this error would be returned is if you attempted to + /// serialize a `chrono::NaiveDate` earlier than the earliest date supported + /// by PostgreSQL. + SerializationError(Box), + + /// Roll back the current transaction. + /// + /// You can return this variant inside of a transaction when you want to + /// roll it back, but have no actual error to return. Diesel will never + /// return this variant unless you gave it to us, and it can be safely + /// ignored in error handling. + RollbackTransaction, + + /// Attempted to perform an operation that cannot be done inside a transaction + /// when a transaction was already open. + AlreadyInTransaction, +} + +#[derive(Debug, Clone, Copy)] +/// The kind of database error that occurred. +/// +/// This is not meant to exhaustively cover all possible errors, but is used to +/// identify errors which are commonly recovered from programmatically. This enum +/// is not intended to be exhaustively matched, and new variants may be added in +/// the future without a major version bump. +pub enum DatabaseErrorKind { + /// A unique constraint was violated. + UniqueViolation, + + /// A foreign key constraint was violated. + ForeignKeyViolation, + + /// The query could not be sent to the database due to a protocol violation. + /// + /// An example of a case where this would occur is if you attempted to send + /// a query with more than 65000 bind parameters using PostgreSQL. + UnableToSendCommand, + + /// A serializable transaction failed to commit due to a read/write + /// dependency on a concurrent transaction. + /// + /// Corresponds to SQLSTATE code 40001 + /// + /// This error is only detected for PostgreSQL, as we do not yet support + /// transaction isolation levels for other backends. + SerializationFailure, + + /// The command could not be completed because the transaction was read + /// only. + /// + /// This error will also be returned for `SELECT` statements which attempted + /// to lock the rows. + ReadOnlyTransaction, + + /// A not null constraint was violated. + NotNullViolation, + + /// A check constraint was violated. + CheckViolation, + + #[doc(hidden)] + __Unknown, // Match against _ instead, more variants may be added in the future +} + +/// Information about an error that was returned by the database. +pub trait DatabaseErrorInformation { + /// The primary human-readable error message. Typically one line. + fn message(&self) -> &str; + + /// An optional secondary error message providing more details about the + /// problem, if it was provided by the database. Might span multiple lines. + fn details(&self) -> Option<&str>; + + /// An optional suggestion of what to do about the problem, if one was + /// provided by the database. + fn hint(&self) -> Option<&str>; + + /// The name of the table the error was associated with, if the error was + /// associated with a specific table and the backend supports retrieving + /// that information. + /// + /// Currently this method will return `None` for all backends other than + /// PostgreSQL. + fn table_name(&self) -> Option<&str>; + + /// The name of the column the error was associated with, if the error was + /// associated with a specific column and the backend supports retrieving + /// that information. + /// + /// Currently this method will return `None` for all backends other than + /// PostgreSQL. + fn column_name(&self) -> Option<&str>; + + /// The constraint that was violated if this error is a constraint violation + /// and the backend supports retrieving that information. + /// + /// Currently this method will return `None` for all backends other than + /// PostgreSQL. + fn constraint_name(&self) -> Option<&str>; +} + +impl fmt::Debug for dyn DatabaseErrorInformation + Send + Sync { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.message(), f) + } +} + +impl DatabaseErrorInformation for String { + fn message(&self) -> &str { + self + } + + fn details(&self) -> Option<&str> { + None + } + fn hint(&self) -> Option<&str> { + None + } + fn table_name(&self) -> Option<&str> { + None + } + fn column_name(&self) -> Option<&str> { + None + } + fn constraint_name(&self) -> Option<&str> { + None + } +} + +/// Errors which can occur during [`Connection::establish`] +/// +/// [`Connection::establish`]: ../connection/trait.Connection.html#tymethod.establish +#[derive(Debug, PartialEq)] +#[non_exhaustive] +pub enum ConnectionError { + /// The connection URL contained a `NUL` byte. + InvalidCString(NulError), + /// The database returned an error. + BadConnection(String), + /// The connection URL could not be parsed. + InvalidConnectionUrl(String), + /// Diesel could not configure the database connection. + /// + /// Diesel may try to automatically set session specific configuration + /// values, such as UTF8 encoding, or enabling the `||` operator on MySQL. + /// This variant is returned if an error occurred executing the query to set + /// those options. Diesel will never affect global configuration. + CouldntSetupConfiguration(Error), +} + +/// A specialized result type for queries. +/// +/// This type is exported by `diesel::prelude`, and is generally used by any +/// code which is interacting with Diesel. This type exists to avoid writing out +/// `diesel::result::Error`, and is otherwise a direct mapping to `Result`. +pub type QueryResult = Result; + +/// A specialized result type for establishing connections. +/// +/// This type exists to avoid writing out `diesel::result::ConnectionError`, and +/// is otherwise a direct mapping to `Result`. +pub type ConnectionResult = Result; + +/// See the [method documentation](#tymethod.optional). +pub trait OptionalExtension { + /// Converts a `QueryResult` into a `QueryResult>`. + /// + /// By default, Diesel treats 0 rows being returned from a query that is expected to return 1 + /// row as an error (e.g. the return value of [`get_result`] or [`first`]). This method will + /// handle that error, and give you back an `Option` instead. + /// + /// [`get_result`]: ../query_dsl/trait.RunQueryDsl.html#method.get_result + /// [`first`]: ../query_dsl/trait.RunQueryDsl.html#method.first + /// + /// # Example + /// + /// ```rust + /// use diesel::{QueryResult, NotFound, OptionalExtension}; + /// + /// let result: QueryResult = Ok(1); + /// assert_eq!(Ok(Some(1)), result.optional()); + /// + /// let result: QueryResult = Err(NotFound); + /// assert_eq!(Ok(None), result.optional()); + /// ``` + fn optional(self) -> Result, Error>; +} + +impl OptionalExtension for QueryResult { + fn optional(self) -> Result, Error> { + match self { + Ok(value) => Ok(Some(value)), + Err(Error::NotFound) => Ok(None), + Err(e) => Err(e), + } + } +} + +impl From for ConnectionError { + fn from(e: NulError) -> Self { + ConnectionError::InvalidCString(e) + } +} + +impl From for Error { + fn from(e: NulError) -> Self { + Error::InvalidCString(e) + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::InvalidCString(ref nul_err) => write!(f, "{}", nul_err), + Error::DatabaseError(_, ref e) => write!(f, "{}", e.message()), + Error::NotFound => f.write_str("Record not found"), + Error::QueryBuilderError(ref e) => e.fmt(f), + Error::DeserializationError(ref e) => e.fmt(f), + Error::SerializationError(ref e) => e.fmt(f), + Error::RollbackTransaction => write!(f, "The current transaction was aborted"), + Error::AlreadyInTransaction => write!( + f, + "Cannot perform this operation while a transaction is open", + ), + } + } +} + +impl StdError for Error { + fn cause(&self) -> Option<&dyn StdError> { + match *self { + Error::InvalidCString(ref e) => Some(e), + Error::QueryBuilderError(ref e) => Some(&**e), + Error::DeserializationError(ref e) => Some(&**e), + Error::SerializationError(ref e) => Some(&**e), + _ => None, + } + } +} + +impl Display for ConnectionError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ConnectionError::InvalidCString(ref nul_err) => nul_err.fmt(f), + ConnectionError::BadConnection(ref s) => write!(f, "{}", s), + ConnectionError::InvalidConnectionUrl(ref s) => write!(f, "{}", s), + ConnectionError::CouldntSetupConfiguration(ref e) => e.fmt(f), + } + } +} + +impl StdError for ConnectionError { + fn cause(&self) -> Option<&dyn StdError> { + match *self { + ConnectionError::InvalidCString(ref e) => Some(e), + ConnectionError::CouldntSetupConfiguration(ref e) => Some(e), + _ => None, + } + } +} + +impl PartialEq for Error { + fn eq(&self, other: &Error) -> bool { + match (self, other) { + (&Error::InvalidCString(ref a), &Error::InvalidCString(ref b)) => a == b, + (&Error::DatabaseError(_, ref a), &Error::DatabaseError(_, ref b)) => { + a.message() == b.message() + } + (&Error::NotFound, &Error::NotFound) => true, + (&Error::RollbackTransaction, &Error::RollbackTransaction) => true, + (&Error::AlreadyInTransaction, &Error::AlreadyInTransaction) => true, + _ => false, + } + } +} + +#[cfg(test)] +#[allow(warnings)] +fn error_impls_send() { + let err: Error = unimplemented!(); + let x: &Send = &err; +} + +pub(crate) fn first_or_not_found(records: QueryResult>) -> QueryResult { + records?.into_iter().next().ok_or(Error::NotFound) +} + +/// An unexpected `NULL` was encountered during deserialization +#[derive(Debug, Clone, Copy)] +pub struct UnexpectedNullError; + +impl fmt::Display for UnexpectedNullError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Unexpected null for non-null column") + } +} + +impl StdError for UnexpectedNullError {} + +/// Expected more fields then present in the current row while deserialising results +#[derive(Debug, Clone, Copy)] +pub struct UnexpectedEndOfRow; + +impl fmt::Display for UnexpectedEndOfRow { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Unexpected end of row") + } +} + +impl StdError for UnexpectedEndOfRow {} diff --git a/collector/benchmarks/diesel/diesel/src/row.rs b/collector/benchmarks/diesel/diesel/src/row.rs new file mode 100644 index 000000000..398372d5c --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/row.rs @@ -0,0 +1,205 @@ +//! Contains the `Row` trait + +use crate::{ + backend::{self, Backend}, + deserialize, +}; +use deserialize::FromSql; +use std::ops::Range; + +/// Representing a way to index into database rows +/// +/// * Crates using existing backends should use existing implementations of +/// this traits. Diesel provides `RowIndex` and `RowIndex<&str>` for +/// all bulit-in backends +/// +/// * Crates implementing custom backends need to provide `RowIndex` and +/// `RowIndex<&str>` impls for their [`Row`] type. +/// +/// [`Row`]: trait.Row.html +pub trait RowIndex { + /// Get the numeric index inside the current row for the provided index value + fn idx(&self, idx: I) -> Option; +} + +/// Represents a single database row. +/// +/// This trait is used as an argument to [`FromSqlRow`]. +/// +/// [`FromSqlRow`]: ../deserialize/trait.FromSqlRow.html +pub trait Row<'a, DB: Backend>: RowIndex + for<'b> RowIndex<&'b str> + Sized { + /// Field type returned by a `Row` implementation + /// + /// * Crates using existing backend should not concern themself with the + /// concrete type of this associated type. + /// + /// * Crates implementing custom backends should provide their own type + /// meeting the required trait bounds + type Field: Field<'a, DB>; + + /// Return type of `PartialRow` + /// + /// For all implementations, beside of the `Row` implementation on `PartialRow` itself + /// this should be `Self`. + #[doc(hidden)] + type InnerPartialRow: Row<'a, DB>; + + /// Get the number of fields in the current row + fn field_count(&self) -> usize; + + /// Get the field with the provided index from the row. + /// + /// Returns `None` if there is no matching field for the given index + fn get(&self, idx: I) -> Option + where + Self: RowIndex; + + /// Returns a wrapping row that allows only to access fields, where the index is part of + /// the provided range. + #[doc(hidden)] + fn partial_row(&self, range: Range) -> PartialRow; +} + +/// Represents a single field in a database row. +/// +/// This trait allows retrieving information on the name of the colum and on the value of the +/// field. +pub trait Field<'a, DB: Backend> { + /// The name of the current field + /// + /// Returns `None` if it's an unnamed field + fn field_name(&self) -> Option<&'a str>; + + /// Get the value representing the current field in the raw representation + /// as it is transmitted by the database + fn value(&self) -> Option>; + + /// Checks whether this field is null or not. + fn is_null(&self) -> bool { + self.value().is_none() + } +} + +/// A row type that wraps an inner row +/// +/// This type only allows to access fields of the inner row, whose index is +/// part of `range`. +/// +/// Indexing via `usize` starts with 0 for this row type. The index is then shifted +/// by `self.range.start` to match the corresponding field in the underlying row. +#[derive(Debug)] +#[doc(hidden)] +pub struct PartialRow<'a, R> { + inner: &'a R, + range: Range, +} + +impl<'a, R> PartialRow<'a, R> { + #[doc(hidden)] + pub fn new<'b, DB>(inner: &'a R, range: Range) -> Self + where + R: Row<'b, DB>, + DB: Backend, + { + let range_lower = std::cmp::min(range.start, inner.field_count()); + let range_upper = std::cmp::min(range.end, inner.field_count()); + Self { + inner, + range: range_lower..range_upper, + } + } +} + +impl<'a, 'b, DB, R> Row<'a, DB> for PartialRow<'b, R> +where + DB: Backend, + R: Row<'a, DB>, +{ + type Field = R::Field; + type InnerPartialRow = R; + + fn field_count(&self) -> usize { + self.range.len() + } + + fn get(&self, idx: I) -> Option + where + Self: RowIndex, + { + let idx = self.idx(idx)?; + self.inner.get(idx) + } + + fn partial_row(&self, range: Range) -> PartialRow { + let range_upper_bound = std::cmp::min(self.range.end, self.range.start + range.end); + let range = (self.range.start + range.start)..range_upper_bound; + PartialRow { + inner: self.inner, + range, + } + } +} + +impl<'a, 'b, R> RowIndex<&'a str> for PartialRow<'b, R> +where + R: RowIndex<&'a str>, +{ + fn idx(&self, idx: &'a str) -> Option { + let idx = self.inner.idx(idx)?; + if self.range.contains(&idx) { + Some(idx) + } else { + None + } + } +} + +impl<'a, R> RowIndex for PartialRow<'a, R> +where + R: RowIndex, +{ + fn idx(&self, idx: usize) -> Option { + let idx = self.inner.idx(idx + self.range.start)?; + if self.range.contains(&idx) { + Some(idx) + } else { + None + } + } +} + +/// Represents a row of a SQL query, where the values are accessed by name +/// rather than by index. +/// +/// This trait is used by implementations of +/// [`QueryableByName`](../deserialize/trait.QueryableByName.html) +pub trait NamedRow<'a, DB: Backend>: Row<'a, DB> { + /// Retrieve and deserialize a single value from the query + /// + /// Note that `ST` *must* be the exact type of the value with that name in + /// the query. The compiler will not be able to verify that you have + /// provided the correct type. If there is a mismatch, you may receive an + /// incorrect value, or a runtime error. + /// + /// If two or more fields in the query have the given name, the result of + /// this function is undefined. + fn get<'b, ST, T>(&self, column_name: &'b str) -> deserialize::Result + where + T: FromSql; +} + +impl<'a, R, DB> NamedRow<'a, DB> for R +where + R: Row<'a, DB>, + DB: Backend, +{ + fn get(&self, column_name: &str) -> deserialize::Result + where + T: FromSql, + { + let field = Row::get(self, column_name) + .ok_or_else(|| format!("Column `{}` was not present in query", column_name))?; + + T::from_nullable_sql(field.value()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/serialize.rs b/collector/benchmarks/diesel/diesel/src/serialize.rs new file mode 100644 index 000000000..d87ca1815 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/serialize.rs @@ -0,0 +1,205 @@ +//! Types and traits related to serializing values for the database + +use std::error::Error; +use std::fmt; +use std::io::{self, Write}; +use std::ops::{Deref, DerefMut}; +use std::result; + +use crate::backend::Backend; +use crate::sql_types::TypeMetadata; + +#[cfg(feature = "postgres")] +pub use crate::pg::serialize::*; + +/// A specialized result type representing the result of serializing +/// a value for the database. +pub type Result = result::Result>; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +/// Tiny enum to make the return type of `ToSql` more descriptive +pub enum IsNull { + /// No data was written, as this type is null + Yes, + /// The value is not null + /// + /// This does not necessarily mean that any data was written to the buffer. + /// For example, an empty string has no data to be sent over the wire, but + /// also is not null. + No, +} + +/// Wraps a buffer to be written by `ToSql` with additional backend specific +/// utilities. +#[derive(Clone, Copy)] +pub struct Output<'a, T, DB> +where + DB: TypeMetadata, + DB::MetadataLookup: 'a, +{ + out: T, + metadata_lookup: Option<&'a DB::MetadataLookup>, +} + +impl<'a, T, DB: TypeMetadata> Output<'a, T, DB> { + /// Construct a new `Output` + pub fn new(out: T, metadata_lookup: &'a DB::MetadataLookup) -> Self { + Output { + out, + metadata_lookup: Some(metadata_lookup), + } + } + + /// Create a new `Output` with the given buffer + pub fn with_buffer(&self, new_out: U) -> Output<'a, U, DB> { + Output { + out: new_out, + metadata_lookup: self.metadata_lookup, + } + } + + /// Return the raw buffer this type is wrapping + pub fn into_inner(self) -> T { + self.out + } + + /// Returns the backend's mechanism for dynamically looking up type + /// metadata at runtime, if relevant for the given backend. + pub fn metadata_lookup(&self) -> &'a DB::MetadataLookup { + self.metadata_lookup.expect("Lookup is there") + } +} + +#[cfg(test)] +impl Output<'static, Vec, DB> { + /// Returns a `Output` suitable for testing `ToSql` implementations. + /// Unsafe to use for testing types which perform dynamic metadata lookup. + pub fn test() -> Self { + Self { + out: Vec::new(), + metadata_lookup: None, + } + } +} + +impl<'a, T: Write, DB: TypeMetadata> Write for Output<'a, T, DB> { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.out.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.out.flush() + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.out.write_all(buf) + } + + fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> { + self.out.write_fmt(fmt) + } +} + +impl<'a, T, DB: TypeMetadata> Deref for Output<'a, T, DB> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.out + } +} + +impl<'a, T, DB: TypeMetadata> DerefMut for Output<'a, T, DB> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.out + } +} + +impl<'a, T, U, DB> PartialEq for Output<'a, T, DB> +where + DB: TypeMetadata, + T: PartialEq, +{ + fn eq(&self, rhs: &U) -> bool { + self.out == *rhs + } +} + +impl<'a, T, DB> fmt::Debug for Output<'a, T, DB> +where + T: fmt::Debug, + DB: TypeMetadata, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.out.fmt(f) + } +} + +/// Serializes a single value to be sent to the database. +/// +/// The output is sent as a bind parameter, and the data must be written in the +/// expected format for the given backend. +/// +/// When possible, implementations of this trait should prefer using an existing +/// implementation, rather than writing to `out` directly. (For example, if you +/// are implementing this for an enum, which is represented as an integer in the +/// database, you should use `i32::to_sql(x, out)` instead of writing to `out` +/// yourself. +/// +/// Any types which implement this trait should also [`#[derive(AsExpression)]`]. +/// +/// ### Backend specific details +/// +/// - For PostgreSQL, the bytes will be sent using the binary protocol, not text. +/// - For SQLite, all implementations should be written in terms of an existing +/// `ToSql` implementation. +/// - For MySQL, the expected bytes will depend on the return value of +/// `type_metadata` for the given SQL type. See [`MysqlType`] for details. +/// - For third party backends, consult that backend's documentation. +/// +/// [`MysqlType`]: ../mysql/enum.MysqlType.html +/// [`#[derive(AsExpression)]`]: ../expression/derive.AsExpression.html; +/// +/// ### Examples +/// +/// Most implementations of this trait will be defined in terms of an existing +/// implementation. +/// +/// ```rust +/// # use diesel::backend::Backend; +/// # use diesel::expression::AsExpression; +/// # use diesel::sql_types::*; +/// # use diesel::serialize::{self, ToSql, Output}; +/// # use std::io::Write; +/// # +/// #[repr(i32)] +/// #[derive(Debug, Clone, Copy, AsExpression)] +/// #[sql_type = "Integer"] +/// pub enum MyEnum { +/// A = 1, +/// B = 2, +/// } +/// +/// impl ToSql for MyEnum +/// where +/// DB: Backend, +/// i32: ToSql, +/// { +/// fn to_sql(&self, out: &mut Output) -> serialize::Result { +/// (*self as i32).to_sql(out) +/// } +/// } +/// ``` +pub trait ToSql: fmt::Debug { + /// See the trait documentation. + fn to_sql(&self, out: &mut Output) -> Result; +} + +impl<'a, A, T, DB> ToSql for &'a T +where + DB: Backend, + T: ToSql + ?Sized, +{ + fn to_sql(&self, out: &mut Output) -> Result { + (*self).to_sql(out) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/sql_types/fold.rs b/collector/benchmarks/diesel/diesel/src/sql_types/fold.rs new file mode 100644 index 000000000..91e0de2e8 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sql_types/fold.rs @@ -0,0 +1,47 @@ +use crate::sql_types::{self, is_nullable, SingleValue, SqlType}; + +/// Represents SQL types which can be used with `SUM` and `AVG` +pub trait Foldable: SingleValue { + /// The SQL type of `sum(this_type)` + type Sum: SqlType + SingleValue; + /// The SQL type of `avg(this_type)` + type Avg: SqlType + SingleValue; +} + +impl Foldable for sql_types::Nullable +where + T: Foldable + SqlType, +{ + type Sum = T::Sum; + type Avg = T::Avg; +} + +macro_rules! foldable_impls { + ($($Source:ty => ($SumType:ty, $AvgType:ty)),+,) => { + $( + impl Foldable for $Source { + type Sum = sql_types::Nullable<$SumType>; + type Avg = sql_types::Nullable<$AvgType>; + } + )+ + } +} + +foldable_impls! { + sql_types::SmallInt => (sql_types::BigInt, sql_types::Numeric), + sql_types::Integer => (sql_types::BigInt, sql_types::Numeric), + sql_types::BigInt => (sql_types::Numeric, sql_types::Numeric), + + sql_types::Float => (sql_types::Float, sql_types::Double), + sql_types::Double => (sql_types::Double, sql_types::Double), + sql_types::Numeric => (sql_types::Numeric, sql_types::Numeric), + + sql_types::Interval => (sql_types::Interval, sql_types::Interval), +} + +#[cfg(feature = "mysql")] +foldable_impls! { + sql_types::Unsigned => (sql_types::Unsigned, sql_types::Numeric), + sql_types::Unsigned => (sql_types::Unsigned, sql_types::Numeric), + sql_types::Unsigned => (sql_types::Numeric, sql_types::Numeric), +} diff --git a/collector/benchmarks/diesel/diesel/src/sql_types/mod.rs b/collector/benchmarks/diesel/diesel/src/sql_types/mod.rs new file mode 100644 index 000000000..db517968d --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sql_types/mod.rs @@ -0,0 +1,632 @@ +//! Types which represent a SQL data type. +//! +//! The structs in this module are *only* used as markers to represent a SQL type. +//! They should never be used in your structs. +//! If you'd like to know the rust types which can be used for a given SQL type, +//! see the documentation for that SQL type. +//! Additional types may be provided by other crates. +//! +//! To see which SQL type can be used with a given Rust type, +//! see the "Implementors" section of [`FromSql`]. +//! +//! [`FromSql`]: ../deserialize/trait.FromSql.html +//! +//! Any backend specific types are re-exported through this module + +mod fold; +pub mod ops; +mod ord; + +pub use self::fold::Foldable; +pub use self::ord::SqlOrd; + +use crate::expression::TypedExpressionType; +use crate::query_builder::QueryId; + +/// The boolean SQL type. +/// +/// On backends without a native boolean type, +/// this is emulated with the smallest supported integer. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`bool`][bool] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`bool`][bool] +/// +/// [bool]: https://doc.rust-lang.org/nightly/std/primitive.bool.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "16", array_oid = "1000")] +#[sqlite_type = "Integer"] +#[mysql_type = "Tiny"] +pub struct Bool; + +/// The tiny integer SQL type. +/// +/// This is only available on MySQL. +/// Keep in mind that `infer_schema!` will see `TINYINT(1)` as `Bool`, +/// not `TinyInt`. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`i8`][i8] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`i8`][i8] +/// +/// [i8]: https://doc.rust-lang.org/nightly/std/primitive.i8.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[mysql_type = "Tiny"] +pub struct TinyInt; +#[doc(hidden)] +pub type Tinyint = TinyInt; + +/// The small integer SQL type. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`i16`][i16] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`i16`][i16] +/// +/// [i16]: https://doc.rust-lang.org/nightly/std/primitive.i16.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "21", array_oid = "1005")] +#[sqlite_type = "SmallInt"] +#[mysql_type = "Short"] +pub struct SmallInt; +#[doc(hidden)] +pub type Int2 = SmallInt; +#[doc(hidden)] +pub type Smallint = SmallInt; + +/// The integer SQL type. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`i32`][i32] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`i32`][i32] +/// +/// [i32]: https://doc.rust-lang.org/nightly/std/primitive.i32.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "23", array_oid = "1007")] +#[sqlite_type = "Integer"] +#[mysql_type = "Long"] +pub struct Integer; +#[doc(hidden)] +pub type Int4 = Integer; + +/// The big integer SQL type. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`i64`][i64] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`i64`][i64] +/// +/// [i64]: https://doc.rust-lang.org/nightly/std/primitive.i64.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "20", array_oid = "1016")] +#[sqlite_type = "Long"] +#[mysql_type = "LongLong"] +pub struct BigInt; +#[doc(hidden)] +pub type Int8 = BigInt; +#[doc(hidden)] +pub type Bigint = BigInt; + +/// The float SQL type. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`f32`][f32] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`f32`][f32] +/// +/// [f32]: https://doc.rust-lang.org/nightly/std/primitive.f32.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "700", array_oid = "1021")] +#[sqlite_type = "Float"] +#[mysql_type = "Float"] +pub struct Float; +#[doc(hidden)] +pub type Float4 = Float; + +/// The double precision float SQL type. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`f64`][f64] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`f64`][f64] +/// +/// [f64]: https://doc.rust-lang.org/nightly/std/primitive.f64.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "701", array_oid = "1022")] +#[sqlite_type = "Double"] +#[mysql_type = "Double"] +pub struct Double; +#[doc(hidden)] +pub type Float8 = Double; + +/// The arbitrary precision numeric SQL type. +/// +/// This type is only supported on PostgreSQL and MySQL. +/// On SQLite, [`Double`](struct.Double.html) should be used instead. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`bigdecimal::BigDecimal`] with `feature = ["numeric"]` +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`bigdecimal::BigDecimal`] with `feature = ["numeric"]` +/// +/// [`bigdecimal::BigDecimal`]: /bigdecimal/struct.BigDecimal.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "1700", array_oid = "1231")] +#[mysql_type = "Numeric"] +#[sqlite_type = "Double"] +pub struct Numeric; + +/// Alias for `Numeric` +pub type Decimal = Numeric; + +/// The text SQL type. +/// +/// On all backends strings must be valid UTF-8. +/// On PostgreSQL strings must not include nul bytes. +/// +/// Schema inference will treat all variants of `TEXT` as this type (e.g. +/// `VARCHAR`, `MEDIUMTEXT`, etc). +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`String`][String] +/// - [`&str`][str] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`String`][String] +/// +/// [String]: https://doc.rust-lang.org/nightly/std/string/struct.String.html +/// [str]: https://doc.rust-lang.org/nightly/std/primitive.str.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "25", array_oid = "1009")] +#[sqlite_type = "Text"] +#[mysql_type = "String"] +pub struct Text; + +/// The SQL `VARCHAR` type +/// +/// This type is generally interchangeable with `TEXT`, so Diesel has this as an +/// alias rather than a separate type (Diesel does not currently support +/// implicit coercions). +/// +/// One notable exception to this is with arrays on PG. `TEXT[]` cannot be +/// coerced to `VARCHAR[]`. It is recommended that you always use `TEXT[]` if +/// you need a string array on PG. +pub type VarChar = Text; +#[doc(hidden)] +pub type Varchar = VarChar; +#[doc(hidden)] +pub type Char = Text; +#[doc(hidden)] +pub type Tinytext = Text; +#[doc(hidden)] +pub type Mediumtext = Text; +#[doc(hidden)] +pub type Longtext = Text; + +/// The binary SQL type. +/// +/// Schema inference will treat all variants of `BLOB` as this type (e.g. +/// `VARBINARY`, `MEDIUMBLOB`, etc). +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`Vec`][Vec] +/// - [`&[u8]`][slice] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`Vec`][Vec] +/// +/// [Vec]: https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html +/// [slice]: https://doc.rust-lang.org/nightly/std/primitive.slice.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "17", array_oid = "1001")] +#[sqlite_type = "Binary"] +#[mysql_type = "Blob"] +pub struct Binary; + +#[doc(hidden)] +pub type Tinyblob = Binary; +#[doc(hidden)] +pub type Blob = Binary; +#[doc(hidden)] +pub type Mediumblob = Binary; +#[doc(hidden)] +pub type Longblob = Binary; +#[doc(hidden)] +pub type Varbinary = Binary; +#[doc(hidden)] +pub type Bit = Binary; + +/// The date SQL type. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`chrono::NaiveDate`][NaiveDate] with `feature = "chrono"` +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`chrono::NaiveDate`][NaiveDate] with `feature = "chrono"` +/// +/// [NaiveDate]: /chrono/naive/date/struct.NaiveDate.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "1082", array_oid = "1182")] +#[sqlite_type = "Text"] +#[mysql_type = "Date"] +pub struct Date; + +/// The interval SQL type. +/// +/// This type is currently only implemented for PostgreSQL. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`PgInterval`] which can be constructed using [`IntervalDsl`] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`PgInterval`] which can be constructed using [`IntervalDsl`] +/// +/// [`PgInterval`]: ../pg/data_types/struct.PgInterval.html +/// [`IntervalDsl`]: ../pg/expression/extensions/trait.IntervalDsl.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "1186", array_oid = "1187")] +pub struct Interval; + +/// The time SQL type. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`chrono::NaiveTime`][NaiveTime] with `feature = "chrono"` +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`chrono::NaiveTime`][NaiveTime] with `feature = "chrono"` +/// +/// [NaiveTime]: /chrono/naive/time/struct.NaiveTime.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "1083", array_oid = "1183")] +#[sqlite_type = "Text"] +#[mysql_type = "Time"] +pub struct Time; + +/// The timestamp SQL type. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`std::time::SystemTime`][SystemTime] (PG only) +/// - [`chrono::NaiveDateTime`][NaiveDateTime] with `feature = "chrono"` +/// - [`time::Timespec`][Timespec] with `feature = "deprecated-time"` (PG only) +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`std::time::SystemTime`][SystemTime] (PG only) +/// - [`chrono::NaiveDateTime`][NaiveDateTime] with `feature = "chrono"` +/// - [`time::Timespec`][Timespec] with `feature = "deprecated-time"` (PG only) +/// +/// [SystemTime]: https://doc.rust-lang.org/nightly/std/time/struct.SystemTime.html +/// [NaiveDateTime]: /chrono/naive/datetime/struct.NaiveDateTime.html +/// [Timespec]: /time/struct.Timespec.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "1114", array_oid = "1115")] +#[sqlite_type = "Text"] +#[mysql_type = "Timestamp"] +pub struct Timestamp; + +/// The JSON SQL type. This type can only be used with `feature = +/// "serde_json"` +/// +/// For postgresql you should normally prefer [`Jsonb`](struct.Jsonb.html) instead, +/// for the reasons discussed there. +/// +/// ### [`ToSql`] impls +/// +/// - [`serde_json::Value`] +/// +/// ### [`FromSql`] impls +/// +/// - [`serde_json::Value`] +/// +/// [`ToSql`]: /serialize/trait.ToSql.html +/// [`FromSql`]: /deserialize/trait.FromSql.html +/// [`serde_json::Value`]: /../serde_json/value/enum.Value.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "114", array_oid = "199")] +#[mysql_type = "String"] +pub struct Json; + +/// The nullable SQL type. +/// +/// This wraps another SQL type to indicate that it can be null. +/// By default all values are assumed to be `NOT NULL`. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - Any `T` which implements `ToSql` +/// - `Option` for any `T` which implements `ToSql` +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - `Option` for any `T` which implements `FromSql` +#[derive(Debug, Clone, Copy, Default)] +pub struct Nullable(ST); + +impl SqlType for Nullable +where + ST: SqlType, +{ + type IsNull = is_nullable::IsNullable; +} + +#[cfg(feature = "postgres")] +pub use crate::pg::types::sql_types::*; + +#[cfg(feature = "mysql")] +pub use crate::mysql::types::*; + +/// Indicates that a SQL type exists for a backend. +/// +/// This trait can be derived using the [`SqlType` derive](derive.SqlType.html) +/// +/// # Example +/// +/// ```rust +/// #[derive(diesel::sql_types::SqlType)] +/// #[postgres(oid = "23", array_oid = "1007")] +/// #[sqlite_type = "Integer"] +/// #[mysql_type = "Long"] +/// pub struct Integer; +/// ``` +pub trait HasSqlType: TypeMetadata { + /// Fetch the metadata for the given type + /// + /// This method may use `lookup` to do dynamic runtime lookup. Implementors + /// of this method should not do dynamic lookup unless absolutely necessary + fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata; +} + +/// Information about how a backend stores metadata about given SQL types +pub trait TypeMetadata { + /// The actual type used to represent metadata. + /// + /// On PostgreSQL, this is the type's OID. + /// On MySQL and SQLite, this is an enum representing all storage classes + /// they support. + type TypeMetadata; + /// The type used for runtime lookup of metadata. + /// + /// For most backends, which don't support user defined types, this will + /// be `()`. + type MetadataLookup; +} + +/// Converts a type which may or may not be nullable into its nullable +/// representation. +pub trait IntoNullable { + /// The nullable representation of this type. + /// + /// For all types except `Nullable`, this will be `Nullable`. + type Nullable; +} + +impl IntoNullable for T +where + T: SqlType + SingleValue, +{ + type Nullable = Nullable; +} + +impl IntoNullable for Nullable +where + T: SqlType, +{ + type Nullable = Self; +} + +/// Converts a type which may or may not be nullable into its not nullable +/// representation. +pub trait IntoNotNullable { + /// The not nullable representation of this type. + /// + /// For `Nullable`, this will be `T` otherwise the type itself + type NotNullable; +} + +impl IntoNotNullable for T +where + T: SqlType, +{ + type NotNullable = T; +} + +impl IntoNotNullable for Nullable +where + T: SqlType, +{ + type NotNullable = T; +} + +/// A marker trait indicating that a SQL type represents a single value, as +/// opposed to a list of values. +/// +/// This trait should generally be implemented for all SQL types with the +/// exception of Rust tuples. If a column could have this as its type, this +/// trait should be implemented. +/// +/// # Deriving +/// +/// This trait is automatically implemented by [`#[derive(SqlType)]`] +/// +/// [`#[derive(SqlType)]`]: derive.SqlType.html +pub trait SingleValue: SqlType {} + +impl SingleValue for Nullable {} + +#[doc(inline)] +pub use diesel_derives::DieselNumericOps; +#[doc(inline)] +pub use diesel_derives::SqlType; + +/// A marker trait for SQL types +/// +/// # Deriving +/// +/// This trait is automatically implemented by [`#[derive(SqlType)]`] +/// which sets `IsNull` to [`is_nullable::NotNull`] +/// +/// [`#[derive(SqlType)]`]: derive.SqlType.html +/// [`is_nullable::NotNull`]: is_nullable/struct.NotNull.html +pub trait SqlType { + /// Is this type nullable? + /// + /// This type should always be one of the structs in the ['is_nullable`] + /// module. See the documentation of those structs for more details. + /// + /// ['is_nullable`]: is_nullable/index.html + type IsNull: OneIsNullable + OneIsNullable; +} + +/// Is one value of `IsNull` nullable? +/// +/// You should never implement this trait. +pub trait OneIsNullable { + /// See the trait documentation + type Out: OneIsNullable + OneIsNullable; +} + +/// Are both values of `IsNull` are nullable? +pub trait AllAreNullable { + /// See the trait documentation + type Out: AllAreNullable + AllAreNullable; +} + +/// A type level constructor for maybe nullable types +/// +/// Constructs either `Nullable` (for `Self` == `is_nullable::IsNullable`) +/// or `O` (for `Self` == `is_nullable::NotNull`) +pub trait MaybeNullableType { + /// See the trait documentation + type Out: SqlType + TypedExpressionType; +} + +/// Possible values for `SqlType::IsNullable` +pub mod is_nullable { + use super::*; + + /// No, this type cannot be null as it is marked as `NOT NULL` at database level + /// + /// This should be choosen for basically all manual impls of `SqlType` + /// beside implementing your own `Nullable<>` wrapper type + #[derive(Debug, Clone, Copy)] + pub struct NotNull; + + /// Yes, this type can be null + /// + /// The only diesel provided `SqlType` that uses this value is [`Nullable`] + /// + /// [`Nullable`]: ../struct.Nullable.html + #[derive(Debug, Clone, Copy)] + pub struct IsNullable; + + impl OneIsNullable for NotNull { + type Out = NotNull; + } + + impl OneIsNullable for NotNull { + type Out = IsNullable; + } + + impl OneIsNullable for IsNullable { + type Out = IsNullable; + } + + impl OneIsNullable for IsNullable { + type Out = IsNullable; + } + + impl AllAreNullable for NotNull { + type Out = NotNull; + } + + impl AllAreNullable for NotNull { + type Out = NotNull; + } + + impl AllAreNullable for IsNullable { + type Out = NotNull; + } + + impl AllAreNullable for IsNullable { + type Out = IsNullable; + } + + impl MaybeNullableType for NotNull + where + O: SqlType + TypedExpressionType, + { + type Out = O; + } + + impl MaybeNullableType for IsNullable + where + O: SqlType, + Nullable: TypedExpressionType, + { + type Out = Nullable; + } + + /// Represents the output type of [`MaybeNullableType`](../trait.MaybeNullableType.html) + pub type MaybeNullable = >::Out; + + /// Represents the output type of [`OneIsNullable`](../trait.OneIsNullable.html) + /// for two given SQL types + pub type IsOneNullable = + as OneIsNullable>>::Out; + + /// Represents the output type of [`AllAreNullable`](../trait.AllAreNullable.html) + /// for two given SQL types + pub type AreAllNullable = + as AllAreNullable>>::Out; + + /// Represents if the SQL type is nullable or not + pub type IsSqlTypeNullable = ::IsNull; +} + +/// A marker trait for accepting expressions of the type `Bool` and +/// `Nullable` in the same place +pub trait BoolOrNullableBool {} + +impl BoolOrNullableBool for Bool {} +impl BoolOrNullableBool for Nullable {} + +#[doc(inline)] +pub use crate::expression::expression_types::Untyped; diff --git a/collector/benchmarks/diesel/diesel/src/sql_types/ops.rs b/collector/benchmarks/diesel/diesel/src/sql_types/ops.rs new file mode 100644 index 000000000..f548764c6 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sql_types/ops.rs @@ -0,0 +1,184 @@ +//! Represents the output of numeric operators in SQL +//! +//! Apps should not need to concern themselves with this module. +//! If you are looking for where the actual implementation of `std::ops::Add` +//! and friends are generated for columns, see +//! [`numeric_expr!`](../../macro.numeric_expr.html). +//! +//! Crates which add new types which allow numeric operators should implement +//! these traits to specify what the output is for a given right hand side. +//! +//! Unlike the traits in `std::ops`, the right hand side is an associated type +//! rather than a type parameter. The biggest drawback of this is that any type +//! can only have one right hand type which can be added/subtracted, etc. The +//! most immediately noticeable effect of this is that you cannot add a nullable +//! number to one that is not nullable. +//! +//! The reason for this is because of the impl of `std::ops::Add` that we need +//! to be able to write. We want the right hand side to allow Rust values which +//! should be sent as bind parameters, not just other Diesel expressions. That +//! means the impl would look like this: +//! +//! ```ignore +//! impl std::ops::Add for my_column +//! where +//! T: AsExpression, +//! my_column::SqlType: diesel::ops::Add, +//! ``` +//! +//! This impl is not valid in Rust, as `ST` is not constrained by the trait or +//! the implementing type. If there were two valid types for `ST` which +//! satisfied all constraints, Rust would not know which one to use, and there +//! would be no way for the user to specify which one should be used. + +use super::*; + +/// Represents SQL types which can be added. +pub trait Add { + /// The SQL type which can be added to this one + type Rhs: SqlType; + /// The SQL type of the result of adding `Rhs` to `Self` + type Output: SqlType; +} + +/// Represents SQL types which can be subtracted. +pub trait Sub { + /// The SQL type which can be subtracted from this one + type Rhs: SqlType; + /// The SQL type of the result of subtracting `Rhs` from `Self` + type Output: SqlType; +} + +/// Represents SQL types which can be multiplied. +pub trait Mul { + /// The SQL type which this can be multiplied by + type Rhs: SqlType; + /// The SQL type of the result of multiplying `Self` by `Rhs` + type Output: SqlType; +} + +/// Represents SQL types which can be divided. +pub trait Div { + /// The SQL type which this one can be divided by + type Rhs: SqlType; + /// The SQL type of the result of dividing `Self` by `Rhs` + type Output: SqlType; +} + +macro_rules! numeric_type { + ($($tpe: ident),*) => { + $( + impl Add for $tpe { + type Rhs = $tpe; + type Output = $tpe; + } + + impl Sub for $tpe { + type Rhs = $tpe; + type Output = $tpe; + } + + impl Mul for $tpe { + type Rhs = $tpe; + type Output = $tpe; + } + + impl Div for $tpe { + type Rhs = $tpe; + type Output = $tpe; + } + )* + } +} + +numeric_type!(SmallInt, Integer, BigInt, Float, Double, Numeric); + +impl Add for Time { + type Rhs = Interval; + type Output = Time; +} + +impl Sub for Time { + type Rhs = Interval; + type Output = Time; +} + +impl Add for Date { + type Rhs = Interval; + type Output = Timestamp; +} + +impl Sub for Date { + type Rhs = Interval; + type Output = Timestamp; +} + +impl Add for Timestamp { + type Rhs = Interval; + type Output = Timestamp; +} + +impl Sub for Timestamp { + type Rhs = Interval; + type Output = Timestamp; +} + +impl Add for Interval { + type Rhs = Interval; + type Output = Interval; +} + +impl Sub for Interval { + type Rhs = Interval; + type Output = Interval; +} + +impl Mul for Interval { + type Rhs = Integer; + type Output = Interval; +} + +impl Div for Interval { + type Rhs = Integer; + type Output = Interval; +} + +impl Add for Nullable +where + T: Add + SqlType, + T::Rhs: SqlType, + T::Output: SqlType, +{ + type Rhs = Nullable; + type Output = Nullable; +} + +impl Sub for Nullable +where + T: Sub + SqlType, + T::Rhs: SqlType, + T::Output: SqlType, +{ + type Rhs = Nullable; + type Output = Nullable; +} + +impl Mul for Nullable +where + T: Mul + SqlType, + T::Rhs: SqlType, + T::Output: SqlType, +{ + type Rhs = Nullable; + type Output = Nullable; +} + +impl Div for Nullable +where + T: Div + SqlType, + T::Rhs: SqlType, + T::Output: SqlType, +{ + type Rhs = Nullable; + type Output = Nullable; +} diff --git a/collector/benchmarks/diesel/diesel/src/sql_types/ord.rs b/collector/benchmarks/diesel/diesel/src/sql_types/ord.rs new file mode 100644 index 000000000..e3ba8d3fd --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sql_types/ord.rs @@ -0,0 +1,30 @@ +use crate::sql_types::{self, is_nullable, SqlType}; + +/// Marker trait for types which can be used with `MAX` and `MIN` +pub trait SqlOrd: SqlType {} + +impl SqlOrd for sql_types::SmallInt {} +impl SqlOrd for sql_types::Integer {} +impl SqlOrd for sql_types::BigInt {} +impl SqlOrd for sql_types::Float {} +impl SqlOrd for sql_types::Double {} +impl SqlOrd for sql_types::Text {} +impl SqlOrd for sql_types::Date {} +impl SqlOrd for sql_types::Interval {} +impl SqlOrd for sql_types::Time {} +impl SqlOrd for sql_types::Timestamp {} +impl SqlOrd for sql_types::Nullable where T: SqlOrd + SqlType {} + +#[cfg(feature = "postgres")] +impl SqlOrd for sql_types::Timestamptz {} +#[cfg(feature = "postgres")] +impl SqlOrd for sql_types::Array {} + +#[cfg(feature = "mysql")] +impl SqlOrd for sql_types::Datetime {} +#[cfg(feature = "mysql")] +impl SqlOrd for sql_types::Unsigned {} +#[cfg(feature = "mysql")] +impl SqlOrd for sql_types::Unsigned {} +#[cfg(feature = "mysql")] +impl SqlOrd for sql_types::Unsigned {} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/backend.rs b/collector/benchmarks/diesel/diesel/src/sqlite/backend.rs new file mode 100644 index 000000000..1c6af1637 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/backend.rs @@ -0,0 +1,57 @@ +//! The SQLite backend +use byteorder::NativeEndian; + +use super::connection::SqliteValue; +use super::query_builder::SqliteQueryBuilder; +use crate::backend::*; +use crate::query_builder::bind_collector::RawBytesBindCollector; +use crate::sql_types::TypeMetadata; + +/// The SQLite backend +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct Sqlite; + +/// Determines how a bind parameter is given to SQLite +/// +/// Diesel deals with bind parameters after serialization as opaque blobs of +/// bytes. However, SQLite instead has several functions where it expects the +/// relevant C types. +/// +/// The variants of this struct determine what bytes are expected from +/// `ToSql` impls. +#[allow(missing_debug_implementations)] +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +pub enum SqliteType { + /// Bind using `sqlite3_bind_blob` + Binary, + /// Bind using `sqlite3_bind_text` + Text, + /// `bytes` should contain an `f32` + Float, + /// `bytes` should contain an `f64` + Double, + /// `bytes` should contain an `i16` + SmallInt, + /// `bytes` should contain an `i32` + Integer, + /// `bytes` should contain an `i64` + Long, +} + +impl Backend for Sqlite { + type QueryBuilder = SqliteQueryBuilder; + type BindCollector = RawBytesBindCollector; + type ByteOrder = NativeEndian; +} + +impl<'a> HasRawValue<'a> for Sqlite { + type RawValue = SqliteValue<'a>; +} + +impl TypeMetadata for Sqlite { + type TypeMetadata = SqliteType; + type MetadataLookup = (); +} + +impl SupportsOnConflictClause for Sqlite {} +impl UsesAnsiSavepointSyntax for Sqlite {} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/connection/diesel_manage_updated_at.sql b/collector/benchmarks/diesel/diesel/src/sqlite/connection/diesel_manage_updated_at.sql new file mode 100644 index 000000000..83c3d33d4 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/connection/diesel_manage_updated_at.sql @@ -0,0 +1,11 @@ +CREATE TRIGGER __diesel_manage_updated_at_{table_name} +AFTER UPDATE ON {table_name} +FOR EACH ROW WHEN + old.updated_at IS NULL AND + new.updated_at IS NULL OR + old.updated_at == new.updated_at +BEGIN + UPDATE {table_name} + SET updated_at = CURRENT_TIMESTAMP + WHERE ROWID = new.ROWID; +END diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/connection/functions.rs b/collector/benchmarks/diesel/diesel/src/sqlite/connection/functions.rs new file mode 100644 index 000000000..c3fccc9df --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/connection/functions.rs @@ -0,0 +1,170 @@ +extern crate libsqlite3_sys as ffi; + +use super::raw::RawConnection; +use super::serialized_value::SerializedValue; +use super::{Sqlite, SqliteAggregateFunction, SqliteValue}; +use crate::deserialize::{FromSqlRow, StaticallySizedRow}; +use crate::result::{DatabaseErrorKind, Error, QueryResult}; +use crate::row::{Field, PartialRow, Row, RowIndex}; +use crate::serialize::{IsNull, Output, ToSql}; +use crate::sql_types::HasSqlType; +use std::marker::PhantomData; + +pub fn register( + conn: &RawConnection, + fn_name: &str, + deterministic: bool, + mut f: F, +) -> QueryResult<()> +where + F: FnMut(&RawConnection, Args) -> Ret + Send + 'static, + Args: FromSqlRow + StaticallySizedRow, + Ret: ToSql, + Sqlite: HasSqlType, +{ + let fields_needed = Args::FIELD_COUNT; + if fields_needed > 127 { + return Err(Error::DatabaseError( + DatabaseErrorKind::UnableToSendCommand, + Box::new("SQLite functions cannot take more than 127 parameters".to_string()), + )); + } + + conn.register_sql_function(fn_name, fields_needed, deterministic, move |conn, args| { + let args = build_sql_function_args::(args)?; + + let result = f(conn, args); + + process_sql_function_result::(result) + })?; + Ok(()) +} + +pub fn register_aggregate( + conn: &RawConnection, + fn_name: &str, +) -> QueryResult<()> +where + A: SqliteAggregateFunction + 'static + Send, + Args: FromSqlRow + StaticallySizedRow, + Ret: ToSql, + Sqlite: HasSqlType, +{ + let fields_needed = Args::FIELD_COUNT; + if fields_needed > 127 { + return Err(Error::DatabaseError( + DatabaseErrorKind::UnableToSendCommand, + Box::new("SQLite functions cannot take more than 127 parameters".to_string()), + )); + } + + conn.register_aggregate_function::( + fn_name, + fields_needed, + )?; + + Ok(()) +} + +pub(crate) fn build_sql_function_args( + args: &[*mut ffi::sqlite3_value], +) -> Result +where + Args: FromSqlRow, +{ + let row = FunctionRow::new(args); + Args::build_from_row(&row).map_err(Error::DeserializationError) +} + +pub(crate) fn process_sql_function_result( + result: Ret, +) -> QueryResult +where + Ret: ToSql, + Sqlite: HasSqlType, +{ + let mut buf = Output::new(Vec::new(), &()); + let is_null = result.to_sql(&mut buf).map_err(Error::SerializationError)?; + + let bytes = if let IsNull::Yes = is_null { + None + } else { + Some(buf.into_inner()) + }; + + Ok(SerializedValue { + ty: Sqlite::metadata(&()), + data: bytes, + }) +} + +#[derive(Clone)] +struct FunctionRow<'a> { + args: &'a [*mut ffi::sqlite3_value], +} + +impl<'a> FunctionRow<'a> { + fn new(args: &'a [*mut ffi::sqlite3_value]) -> Self { + Self { args } + } +} + +impl<'a> Row<'a, Sqlite> for FunctionRow<'a> { + type Field = FunctionArgument<'a>; + type InnerPartialRow = Self; + + fn field_count(&self) -> usize { + self.args.len() + } + + fn get(&self, idx: I) -> Option + where + Self: crate::row::RowIndex, + { + let idx = self.idx(idx)?; + + self.args.get(idx).map(|arg| FunctionArgument { + arg: *arg, + p: PhantomData, + }) + } + + fn partial_row(&self, range: std::ops::Range) -> PartialRow { + PartialRow::new(self, range) + } +} + +impl<'a> RowIndex for FunctionRow<'a> { + fn idx(&self, idx: usize) -> Option { + if idx < self.args.len() { + Some(idx) + } else { + None + } + } +} + +impl<'a, 'b> RowIndex<&'a str> for FunctionRow<'b> { + fn idx(&self, _idx: &'a str) -> Option { + None + } +} + +struct FunctionArgument<'a> { + arg: *mut ffi::sqlite3_value, + p: PhantomData<&'a ()>, +} + +impl<'a> Field<'a, Sqlite> for FunctionArgument<'a> { + fn field_name(&self) -> Option<&'a str> { + None + } + + fn is_null(&self) -> bool { + self.value().is_none() + } + + fn value(&self) -> Option> { + unsafe { SqliteValue::new(self.arg) } + } +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/connection/mod.rs b/collector/benchmarks/diesel/diesel/src/sqlite/connection/mod.rs new file mode 100644 index 000000000..a52a1f842 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/connection/mod.rs @@ -0,0 +1,517 @@ +extern crate libsqlite3_sys as ffi; + +mod functions; +#[doc(hidden)] +pub mod raw; +mod serialized_value; +mod sqlite_value; +mod statement_iterator; +mod stmt; + +pub use self::sqlite_value::SqliteValue; + +use std::os::raw as libc; + +use self::raw::RawConnection; +use self::statement_iterator::*; +use self::stmt::{Statement, StatementUse}; +use super::SqliteAggregateFunction; +use crate::connection::*; +use crate::deserialize::{FromSqlRow, StaticallySizedRow}; +use crate::expression::QueryMetadata; +use crate::query_builder::bind_collector::RawBytesBindCollector; +use crate::query_builder::*; +use crate::result::*; +use crate::serialize::ToSql; +use crate::sql_types::HasSqlType; +use crate::sqlite::Sqlite; + +/// Connections for the SQLite backend. Unlike other backends, "connection URLs" +/// for SQLite are file paths, [URIs](https://sqlite.org/uri.html), or special +/// identifiers like `:memory:`. +#[allow(missing_debug_implementations)] +pub struct SqliteConnection { + statement_cache: StatementCache, + raw_connection: RawConnection, + transaction_manager: AnsiTransactionManager, +} + +// This relies on the invariant that RawConnection or Statement are never +// leaked. If a reference to one of those was held on a different thread, this +// would not be thread safe. +unsafe impl Send for SqliteConnection {} + +impl SimpleConnection for SqliteConnection { + fn batch_execute(&self, query: &str) -> QueryResult<()> { + self.raw_connection.exec(query) + } +} + +impl Connection for SqliteConnection { + type Backend = Sqlite; + type TransactionManager = AnsiTransactionManager; + + fn establish(database_url: &str) -> ConnectionResult { + use crate::result::ConnectionError::CouldntSetupConfiguration; + + let raw_connection = RawConnection::establish(database_url)?; + let conn = Self { + statement_cache: StatementCache::new(), + raw_connection, + transaction_manager: AnsiTransactionManager::new(), + }; + conn.register_diesel_sql_functions() + .map_err(CouldntSetupConfiguration)?; + Ok(conn) + } + + #[doc(hidden)] + fn execute(&self, query: &str) -> QueryResult { + self.batch_execute(query)?; + Ok(self.raw_connection.rows_affected_by_last_query()) + } + + #[doc(hidden)] + fn load(&self, source: T) -> QueryResult> + where + T: AsQuery, + T::Query: QueryFragment + QueryId, + U: FromSqlRow, + Self::Backend: QueryMetadata, + { + let mut statement = self.prepare_query(&source.as_query())?; + let statement_use = StatementUse::new(&mut statement); + let iter = StatementIterator::new(statement_use); + iter.collect() + } + + #[doc(hidden)] + fn execute_returning_count(&self, source: &T) -> QueryResult + where + T: QueryFragment + QueryId, + { + let mut statement = self.prepare_query(source)?; + let mut statement_use = StatementUse::new(&mut statement); + statement_use.run()?; + Ok(self.raw_connection.rows_affected_by_last_query()) + } + + #[doc(hidden)] + fn transaction_manager(&self) -> &Self::TransactionManager { + &self.transaction_manager + } +} + +impl SqliteConnection { + /// Run a transaction with `BEGIN IMMEDIATE` + /// + /// This method will return an error if a transaction is already open. + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # let conn = SqliteConnection::establish(":memory:").unwrap(); + /// conn.immediate_transaction(|| { + /// // Do stuff in a transaction + /// Ok(()) + /// }) + /// # } + /// ``` + pub fn immediate_transaction(&self, f: F) -> Result + where + F: FnOnce() -> Result, + E: From, + { + self.transaction_sql(f, "BEGIN IMMEDIATE") + } + + /// Run a transaction with `BEGIN EXCLUSIVE` + /// + /// This method will return an error if a transaction is already open. + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # let conn = SqliteConnection::establish(":memory:").unwrap(); + /// conn.exclusive_transaction(|| { + /// // Do stuff in a transaction + /// Ok(()) + /// }) + /// # } + /// ``` + pub fn exclusive_transaction(&self, f: F) -> Result + where + F: FnOnce() -> Result, + E: From, + { + self.transaction_sql(f, "BEGIN EXCLUSIVE") + } + + fn transaction_sql(&self, f: F, sql: &str) -> Result + where + F: FnOnce() -> Result, + E: From, + { + let transaction_manager = self.transaction_manager(); + + transaction_manager.begin_transaction_sql(self, sql)?; + match f() { + Ok(value) => { + transaction_manager.commit_transaction(self)?; + Ok(value) + } + Err(e) => { + transaction_manager.rollback_transaction(self)?; + Err(e) + } + } + } + + fn prepare_query + QueryId>( + &self, + source: &T, + ) -> QueryResult> { + let mut statement = self.cached_prepared_statement(source)?; + + let mut bind_collector = RawBytesBindCollector::::new(); + source.collect_binds(&mut bind_collector, &())?; + let metadata = bind_collector.metadata; + let binds = bind_collector.binds; + for (tpe, value) in metadata.into_iter().zip(binds) { + statement.bind(tpe, value)?; + } + + Ok(statement) + } + + fn cached_prepared_statement + QueryId>( + &self, + source: &T, + ) -> QueryResult> { + self.statement_cache.cached_statement(source, &[], |sql| { + Statement::prepare(&self.raw_connection, sql) + }) + } + + #[doc(hidden)] + pub fn register_sql_function( + &self, + fn_name: &str, + deterministic: bool, + mut f: F, + ) -> QueryResult<()> + where + F: FnMut(Args) -> Ret + Send + 'static, + Args: FromSqlRow + StaticallySizedRow, + Ret: ToSql, + Sqlite: HasSqlType, + { + functions::register( + &self.raw_connection, + fn_name, + deterministic, + move |_, args| f(args), + ) + } + + #[doc(hidden)] + pub fn register_aggregate_function( + &self, + fn_name: &str, + ) -> QueryResult<()> + where + A: SqliteAggregateFunction + 'static + Send, + Args: FromSqlRow + StaticallySizedRow, + Ret: ToSql, + Sqlite: HasSqlType, + { + functions::register_aggregate::<_, _, _, _, A>(&self.raw_connection, fn_name) + } + + fn register_diesel_sql_functions(&self) -> QueryResult<()> { + use crate::sql_types::{Integer, Text}; + + functions::register::( + &self.raw_connection, + "diesel_manage_updated_at", + false, + |conn, table_name: String| { + conn.exec(&format!( + include_str!("diesel_manage_updated_at.sql"), + table_name = table_name + )) + .expect("Failed to create trigger"); + 0 // have to return *something* + }, + ) + } +} + +fn error_message(err_code: libc::c_int) -> &'static str { + ffi::code_to_str(err_code) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::dsl::sql; + use crate::prelude::*; + use crate::sql_types::Integer; + + #[test] + fn prepared_statements_are_cached_when_run() { + let connection = SqliteConnection::establish(":memory:").unwrap(); + let query = crate::select(1.into_sql::()); + + assert_eq!(Ok(1), query.get_result(&connection)); + assert_eq!(Ok(1), query.get_result(&connection)); + assert_eq!(1, connection.statement_cache.len()); + } + + #[test] + fn sql_literal_nodes_are_not_cached() { + let connection = SqliteConnection::establish(":memory:").unwrap(); + let query = crate::select(sql::("1")); + + assert_eq!(Ok(1), query.get_result(&connection)); + assert_eq!(0, connection.statement_cache.len()); + } + + #[test] + fn queries_containing_sql_literal_nodes_are_not_cached() { + let connection = SqliteConnection::establish(":memory:").unwrap(); + let one_as_expr = 1.into_sql::(); + let query = crate::select(one_as_expr.eq(sql::("1"))); + + assert_eq!(Ok(true), query.get_result(&connection)); + assert_eq!(0, connection.statement_cache.len()); + } + + #[test] + fn queries_containing_in_with_vec_are_not_cached() { + let connection = SqliteConnection::establish(":memory:").unwrap(); + let one_as_expr = 1.into_sql::(); + let query = crate::select(one_as_expr.eq_any(vec![1, 2, 3])); + + assert_eq!(Ok(true), query.get_result(&connection)); + assert_eq!(0, connection.statement_cache.len()); + } + + #[test] + fn queries_containing_in_with_subselect_are_cached() { + let connection = SqliteConnection::establish(":memory:").unwrap(); + let one_as_expr = 1.into_sql::(); + let query = crate::select(one_as_expr.eq_any(crate::select(one_as_expr))); + + assert_eq!(Ok(true), query.get_result(&connection)); + assert_eq!(1, connection.statement_cache.len()); + } + + use crate::sql_types::Text; + sql_function!(fn fun_case(x: Text) -> Text); + + #[test] + fn register_custom_function() { + let connection = SqliteConnection::establish(":memory:").unwrap(); + fun_case::register_impl(&connection, |x: String| { + x.chars() + .enumerate() + .map(|(i, c)| { + if i % 2 == 0 { + c.to_lowercase().to_string() + } else { + c.to_uppercase().to_string() + } + }) + .collect::() + }) + .unwrap(); + + let mapped_string = crate::select(fun_case("foobar")) + .get_result::(&connection) + .unwrap(); + assert_eq!("fOoBaR", mapped_string); + } + + sql_function!(fn my_add(x: Integer, y: Integer) -> Integer); + + #[test] + fn register_multiarg_function() { + let connection = SqliteConnection::establish(":memory:").unwrap(); + my_add::register_impl(&connection, |x: i32, y: i32| x + y).unwrap(); + + let added = crate::select(my_add(1, 2)).get_result::(&connection); + assert_eq!(Ok(3), added); + } + + sql_function!(fn add_counter(x: Integer) -> Integer); + + #[test] + fn register_nondeterministic_function() { + let connection = SqliteConnection::establish(":memory:").unwrap(); + let mut y = 0; + add_counter::register_nondeterministic_impl(&connection, move |x: i32| { + y += 1; + x + y + }) + .unwrap(); + + let added = crate::select((add_counter(1), add_counter(1), add_counter(1))) + .get_result::<(i32, i32, i32)>(&connection); + assert_eq!(Ok((2, 3, 4)), added); + } + + use crate::sqlite::SqliteAggregateFunction; + + sql_function! { + #[aggregate] + fn my_sum(expr: Integer) -> Integer; + } + + #[derive(Default)] + struct MySum { + sum: i32, + } + + impl SqliteAggregateFunction for MySum { + type Output = i32; + + fn step(&mut self, expr: i32) { + self.sum += expr; + } + + fn finalize(aggregator: Option) -> Self::Output { + aggregator.map(|a| a.sum).unwrap_or_default() + } + } + + table! { + my_sum_example { + id -> Integer, + value -> Integer, + } + } + + #[test] + fn register_aggregate_function() { + use self::my_sum_example::dsl::*; + + let connection = SqliteConnection::establish(":memory:").unwrap(); + connection + .execute( + "CREATE TABLE my_sum_example (id integer primary key autoincrement, value integer)", + ) + .unwrap(); + connection + .execute("INSERT INTO my_sum_example (value) VALUES (1), (2), (3)") + .unwrap(); + + my_sum::register_impl::(&connection).unwrap(); + + let result = my_sum_example + .select(my_sum(value)) + .get_result::(&connection); + assert_eq!(Ok(6), result); + } + + #[test] + fn register_aggregate_function_returns_finalize_default_on_empty_set() { + use self::my_sum_example::dsl::*; + + let connection = SqliteConnection::establish(":memory:").unwrap(); + connection + .execute( + "CREATE TABLE my_sum_example (id integer primary key autoincrement, value integer)", + ) + .unwrap(); + + my_sum::register_impl::(&connection).unwrap(); + + let result = my_sum_example + .select(my_sum(value)) + .get_result::(&connection); + assert_eq!(Ok(0), result); + } + + sql_function! { + #[aggregate] + fn range_max(expr1: Integer, expr2: Integer, expr3: Integer) -> Nullable; + } + + #[derive(Default)] + struct RangeMax { + max_value: Option, + } + + impl SqliteAggregateFunction<(T, T, T)> for RangeMax { + type Output = Option; + + fn step(&mut self, (x0, x1, x2): (T, T, T)) { + let max = if x0 >= x1 && x0 >= x2 { + x0 + } else if x1 >= x0 && x1 >= x2 { + x1 + } else { + x2 + }; + + self.max_value = match self.max_value { + Some(current_max_value) if max > current_max_value => Some(max), + None => Some(max), + _ => self.max_value, + }; + } + + fn finalize(aggregator: Option) -> Self::Output { + aggregator?.max_value + } + } + + table! { + range_max_example { + id -> Integer, + value1 -> Integer, + value2 -> Integer, + value3 -> Integer, + } + } + + #[test] + fn register_aggregate_multiarg_function() { + use self::range_max_example::dsl::*; + + let connection = SqliteConnection::establish(":memory:").unwrap(); + connection + .execute( + r#"CREATE TABLE range_max_example ( + id integer primary key autoincrement, + value1 integer, + value2 integer, + value3 integer + )"#, + ) + .unwrap(); + connection.execute("INSERT INTO range_max_example (value1, value2, value3) VALUES (3, 2, 1), (2, 2, 2)").unwrap(); + + range_max::register_impl::, _, _, _>(&connection).unwrap(); + let result = range_max_example + .select(range_max(value1, value2, value3)) + .get_result::>(&connection) + .unwrap(); + assert_eq!(Some(3), result); + } +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/connection/raw.rs b/collector/benchmarks/diesel/diesel/src/sqlite/connection/raw.rs new file mode 100644 index 000000000..7d2484646 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/connection/raw.rs @@ -0,0 +1,390 @@ +extern crate libsqlite3_sys as ffi; + +use std::ffi::{CStr, CString, NulError}; +use std::io::{stderr, Write}; +use std::os::raw as libc; +use std::ptr::NonNull; +use std::{mem, ptr, slice, str}; + +use super::functions::{build_sql_function_args, process_sql_function_result}; +use super::serialized_value::SerializedValue; +use super::{Sqlite, SqliteAggregateFunction}; +use crate::deserialize::FromSqlRow; +use crate::result::Error::DatabaseError; +use crate::result::*; +use crate::serialize::ToSql; +use crate::sql_types::HasSqlType; + +#[allow(missing_debug_implementations, missing_copy_implementations)] +pub struct RawConnection { + pub(crate) internal_connection: NonNull, +} + +impl RawConnection { + pub fn establish(database_url: &str) -> ConnectionResult { + let mut conn_pointer = ptr::null_mut(); + + let database_url = if database_url.starts_with("sqlite://") { + CString::new(database_url.replacen("sqlite://", "file:", 1))? + } else { + CString::new(database_url)? + }; + let flags = ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE | ffi::SQLITE_OPEN_URI; + let connection_status = unsafe { + ffi::sqlite3_open_v2(database_url.as_ptr(), &mut conn_pointer, flags, ptr::null()) + }; + + match connection_status { + ffi::SQLITE_OK => { + let conn_pointer = unsafe { NonNull::new_unchecked(conn_pointer) }; + Ok(RawConnection { + internal_connection: conn_pointer, + }) + } + err_code => { + let message = super::error_message(err_code); + Err(ConnectionError::BadConnection(message.into())) + } + } + } + + pub fn exec(&self, query: &str) -> QueryResult<()> { + let mut err_msg = ptr::null_mut(); + let query = CString::new(query)?; + let callback_fn = None; + let callback_arg = ptr::null_mut(); + unsafe { + ffi::sqlite3_exec( + self.internal_connection.as_ptr(), + query.as_ptr(), + callback_fn, + callback_arg, + &mut err_msg, + ); + } + + if err_msg.is_null() { + Ok(()) + } else { + let msg = convert_to_string_and_free(err_msg); + let error_kind = DatabaseErrorKind::__Unknown; + Err(DatabaseError(error_kind, Box::new(msg))) + } + } + + pub fn rows_affected_by_last_query(&self) -> usize { + unsafe { ffi::sqlite3_changes(self.internal_connection.as_ptr()) as usize } + } + + pub fn register_sql_function( + &self, + fn_name: &str, + num_args: usize, + deterministic: bool, + f: F, + ) -> QueryResult<()> + where + F: FnMut(&Self, &[*mut ffi::sqlite3_value]) -> QueryResult + + Send + + 'static, + { + let fn_name = Self::get_fn_name(fn_name)?; + let flags = Self::get_flags(deterministic); + let callback_fn = Box::into_raw(Box::new(f)); + + let result = unsafe { + ffi::sqlite3_create_function_v2( + self.internal_connection.as_ptr(), + fn_name.as_ptr(), + num_args as _, + flags, + callback_fn as *mut _, + Some(run_custom_function::), + None, + None, + Some(destroy_boxed_fn::), + ) + }; + + Self::process_sql_function_result(result) + } + + pub fn register_aggregate_function( + &self, + fn_name: &str, + num_args: usize, + ) -> QueryResult<()> + where + A: SqliteAggregateFunction + 'static + Send, + Args: FromSqlRow, + Ret: ToSql, + Sqlite: HasSqlType, + { + let fn_name = Self::get_fn_name(fn_name)?; + let flags = Self::get_flags(false); + + let result = unsafe { + ffi::sqlite3_create_function_v2( + self.internal_connection.as_ptr(), + fn_name.as_ptr(), + num_args as _, + flags, + ptr::null_mut(), + None, + Some(run_aggregator_step_function::<_, _, _, _, A>), + Some(run_aggregator_final_function::<_, _, _, _, A>), + None, + ) + }; + + Self::process_sql_function_result(result) + } + + fn get_fn_name(fn_name: &str) -> Result { + Ok(CString::new(fn_name)?) + } + + fn get_flags(deterministic: bool) -> i32 { + let mut flags = ffi::SQLITE_UTF8; + if deterministic { + flags |= ffi::SQLITE_DETERMINISTIC; + } + flags + } + + fn process_sql_function_result(result: i32) -> Result<(), Error> { + if result == ffi::SQLITE_OK { + Ok(()) + } else { + let error_message = super::error_message(result); + Err(DatabaseError( + DatabaseErrorKind::__Unknown, + Box::new(error_message.to_string()), + )) + } + } +} + +impl Drop for RawConnection { + fn drop(&mut self) { + use std::thread::panicking; + + let close_result = unsafe { ffi::sqlite3_close(self.internal_connection.as_ptr()) }; + if close_result != ffi::SQLITE_OK { + let error_message = super::error_message(close_result); + if panicking() { + write!( + stderr(), + "Error closing SQLite connection: {}", + error_message + ) + .expect("Error writing to `stderr`"); + } else { + panic!("Error closing SQLite connection: {}", error_message); + } + } + } +} + +fn convert_to_string_and_free(err_msg: *const libc::c_char) -> String { + let msg = unsafe { + let bytes = CStr::from_ptr(err_msg).to_bytes(); + // sqlite is documented to return utf8 strings here + str::from_utf8_unchecked(bytes).into() + }; + unsafe { ffi::sqlite3_free(err_msg as *mut libc::c_void) }; + msg +} + +#[allow(warnings)] +extern "C" fn run_custom_function( + ctx: *mut ffi::sqlite3_context, + num_args: libc::c_int, + value_ptr: *mut *mut ffi::sqlite3_value, +) where + F: FnMut(&RawConnection, &[*mut ffi::sqlite3_value]) -> QueryResult + + Send + + 'static, +{ + static NULL_DATA_ERR: &str = "An unknown error occurred. sqlite3_user_data returned a null pointer. This should never happen."; + static NULL_CONN_ERR: &str = "An unknown error occurred. sqlite3_context_db_handle returned a null pointer. This should never happen."; + + unsafe { + let data_ptr = ffi::sqlite3_user_data(ctx); + let data_ptr = data_ptr as *mut F; + let f = match data_ptr.as_mut() { + Some(f) => f, + None => { + ffi::sqlite3_result_error( + ctx, + NULL_DATA_ERR.as_ptr() as *const _ as *const _, + NULL_DATA_ERR.len() as _, + ); + return; + } + }; + + let args = slice::from_raw_parts(value_ptr, num_args as _); + let conn = match NonNull::new(ffi::sqlite3_context_db_handle(ctx)) { + Some(conn) => RawConnection { + internal_connection: conn, + }, + None => { + ffi::sqlite3_result_error( + ctx, + NULL_DATA_ERR.as_ptr() as *const _ as *const _, + NULL_DATA_ERR.len() as _, + ); + return; + } + }; + match f(&conn, args) { + Ok(value) => value.result_of(ctx), + Err(e) => { + let msg = e.to_string(); + ffi::sqlite3_result_error(ctx, msg.as_ptr() as *const _, msg.len() as _); + } + } + + mem::forget(conn); + } +} + +// Need a custom option type here, because the std lib one does not have guarantees about the discriminate values +// See: https://github.com/rust-lang/rfcs/blob/master/text/2195-really-tagged-unions.md#opaque-tags +#[repr(u8)] +enum OptionalAggregator { + // Discriminant is 0 + None, + Some(A), +} + +#[allow(warnings)] +extern "C" fn run_aggregator_step_function( + ctx: *mut ffi::sqlite3_context, + num_args: libc::c_int, + value_ptr: *mut *mut ffi::sqlite3_value, +) where + A: SqliteAggregateFunction + 'static + Send, + Args: FromSqlRow, + Ret: ToSql, + Sqlite: HasSqlType, +{ + unsafe { + // This block of unsafe code makes the following assumptions: + // + // * sqlite3_aggregate_context allocates sizeof::> + // bytes of zeroed memory as documented here: + // https://www.sqlite.org/c3ref/aggregate_context.html + // A null pointer is returned for negative or zero sized types, + // which should be impossible in theory. We check that nevertheless + // + // * OptionalAggregator::None has a discriminant of 0 as specified by + // #[repr(u8)] + RFC 2195 + // + // * If all bytes are zero, the discriminant is also zero, so we can + // assume that we get OptionalAggregator::None in this case. This is + // not UB as we only access the discriminant here, so we do not try + // to read any other zeroed memory. After that we initialize our enum + // by writing a correct value at this location via ptr::write_unaligned + // + // * We use ptr::write_unaligned as we did not found any guarantees that + // the memory will have a correct alignment. + // (Note I(weiznich): would assume that it is aligned correctly, but we + // we cannot guarantee it, so better be safe than sorry) + let aggregate_context = ffi::sqlite3_aggregate_context( + ctx, + std::mem::size_of::>() as i32, + ); + let mut aggregate_context = NonNull::new(aggregate_context as *mut OptionalAggregator); + let aggregator = match aggregate_context.map(|a| &mut *a.as_ptr()) { + Some(&mut OptionalAggregator::Some(ref mut agg)) => agg, + Some(mut a_ptr @ &mut OptionalAggregator::None) => { + ptr::write_unaligned(a_ptr as *mut _, OptionalAggregator::Some(A::default())); + if let &mut OptionalAggregator::Some(ref mut agg) = a_ptr { + agg + } else { + unreachable!( + "We've written the aggregator above to that location, it must be there" + ) + } + } + None => { + null_aggregate_context_error(ctx); + return; + } + }; + + let mut f = |args: &[*mut ffi::sqlite3_value]| -> Result<(), Error> { + let args = build_sql_function_args::(args)?; + + Ok(aggregator.step(args)) + }; + + let args = slice::from_raw_parts(value_ptr, num_args as _); + match f(args) { + Err(e) => { + let msg = e.to_string(); + ffi::sqlite3_result_error(ctx, msg.as_ptr() as *const _, msg.len() as _); + } + _ => (), + }; + } +} + +extern "C" fn run_aggregator_final_function( + ctx: *mut ffi::sqlite3_context, +) where + A: SqliteAggregateFunction + 'static + Send, + Args: FromSqlRow, + Ret: ToSql, + Sqlite: HasSqlType, +{ + unsafe { + // Within the xFinal callback, it is customary to set nBytes to 0 so no pointless memory + // allocations occur, a null pointer is returned in this case + // See: https://www.sqlite.org/c3ref/aggregate_context.html + // + // For the reasoning about the safety of the OptionalAggregator handling + // see the comment in run_aggregator_step_function. + let aggregate_context = ffi::sqlite3_aggregate_context(ctx, 0); + let mut aggregate_context = NonNull::new(aggregate_context as *mut OptionalAggregator); + let aggregator = match aggregate_context { + Some(ref mut a) => match std::mem::replace(a.as_mut(), OptionalAggregator::None) { + OptionalAggregator::Some(agg) => Some(agg), + OptionalAggregator::None => unreachable!("We've written to the aggregator in the xStep callback. If xStep was never called, then ffi::sqlite_aggregate_context() would have returned a NULL pointer") + }, + None => None, + }; + + let result = A::finalize(aggregator); + + match process_sql_function_result::(result) { + Ok(value) => value.result_of(ctx), + Err(e) => { + let msg = e.to_string(); + ffi::sqlite3_result_error(ctx, msg.as_ptr() as *const _, msg.len() as _); + } + } + } +} + +unsafe fn null_aggregate_context_error(ctx: *mut ffi::sqlite3_context) { + static NULL_AG_CTX_ERR: &str = "An unknown error occurred. sqlite3_aggregate_context returned a null pointer. This should never happen."; + + ffi::sqlite3_result_error( + ctx, + NULL_AG_CTX_ERR.as_ptr() as *const _ as *const _, + NULL_AG_CTX_ERR.len() as _, + ); +} + +extern "C" fn destroy_boxed_fn(data: *mut libc::c_void) +where + F: FnMut(&RawConnection, &[*mut ffi::sqlite3_value]) -> QueryResult + + Send + + 'static, +{ + let ptr = data as *mut F; + unsafe { Box::from_raw(ptr) }; +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/connection/serialized_value.rs b/collector/benchmarks/diesel/diesel/src/sqlite/connection/serialized_value.rs new file mode 100644 index 000000000..5c38f22be --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/connection/serialized_value.rs @@ -0,0 +1,113 @@ +extern crate libsqlite3_sys as ffi; + +use std::os::raw as libc; +use std::ptr::{self, NonNull}; + +use crate::sqlite::SqliteType; + +pub struct SerializedValue { + pub ty: SqliteType, + pub data: Option>, +} + +impl SerializedValue { + // We are always reading potentially misaligned pointers with + // `ptr::read_unaligned` + #[allow(clippy::cast_ptr_alignment)] + pub(crate) fn bind_to(self, stmt: NonNull, idx: libc::c_int) -> libc::c_int { + // This unsafe block assumes the following invariants: + // + // - `stmt` points to valid memory + // - If `self.ty` is anything other than `Binary` or `Text`, the appropriate + // number of bytes were written to `value` for an integer of the + // corresponding size. + unsafe { + match (self.ty, self.data) { + (_, None) => ffi::sqlite3_bind_null(stmt.as_ptr(), idx), + (SqliteType::Binary, Some(bytes)) => ffi::sqlite3_bind_blob( + stmt.as_ptr(), + idx, + bytes.as_ptr() as *const libc::c_void, + bytes.len() as libc::c_int, + ffi::SQLITE_TRANSIENT(), + ), + (SqliteType::Text, Some(bytes)) => ffi::sqlite3_bind_text( + stmt.as_ptr(), + idx, + bytes.as_ptr() as *const libc::c_char, + bytes.len() as libc::c_int, + ffi::SQLITE_TRANSIENT(), + ), + (SqliteType::Float, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const f32); + ffi::sqlite3_bind_double(stmt.as_ptr(), idx, libc::c_double::from(value)) + } + (SqliteType::Double, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const f64); + ffi::sqlite3_bind_double(stmt.as_ptr(), idx, value as libc::c_double) + } + (SqliteType::SmallInt, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const i16); + ffi::sqlite3_bind_int(stmt.as_ptr(), idx, libc::c_int::from(value)) + } + (SqliteType::Integer, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const i32); + ffi::sqlite3_bind_int(stmt.as_ptr(), idx, value as libc::c_int) + } + (SqliteType::Long, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const i64); + ffi::sqlite3_bind_int64(stmt.as_ptr(), idx, value) + } + } + } + } + + // We are always reading potentially misaligned pointers with + // `ptr::read_unaligned` + #[allow(clippy::cast_ptr_alignment)] + pub fn result_of(self, ctx: *mut ffi::sqlite3_context) { + // This unsafe block assumes the following invariants: + // + // - `ctx` points to valid memory + // - If `self.ty` is anything other than `Binary` or `Text`, the appropriate + // number of bytes were written to `self.data` for an integer of the + // corresponding size. + unsafe { + match (self.ty, self.data) { + (_, None) => ffi::sqlite3_result_null(ctx), + (SqliteType::Binary, Some(bytes)) => ffi::sqlite3_result_blob( + ctx, + bytes.as_ptr() as *const libc::c_void, + bytes.len() as libc::c_int, + ffi::SQLITE_TRANSIENT(), + ), + (SqliteType::Text, Some(bytes)) => ffi::sqlite3_result_text( + ctx, + bytes.as_ptr() as *const libc::c_char, + bytes.len() as libc::c_int, + ffi::SQLITE_TRANSIENT(), + ), + (SqliteType::Float, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const f32); + ffi::sqlite3_result_double(ctx, libc::c_double::from(value)) + } + (SqliteType::Double, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const f64); + ffi::sqlite3_result_double(ctx, value as libc::c_double) + } + (SqliteType::SmallInt, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const i16); + ffi::sqlite3_result_int(ctx, libc::c_int::from(value)) + } + (SqliteType::Integer, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const i32); + ffi::sqlite3_result_int(ctx, value as libc::c_int) + } + (SqliteType::Long, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const i64); + ffi::sqlite3_result_int64(ctx, value) + } + } + } + } +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/connection/sqlite_value.rs b/collector/benchmarks/diesel/diesel/src/sqlite/connection/sqlite_value.rs new file mode 100644 index 000000000..60e898852 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/connection/sqlite_value.rs @@ -0,0 +1,189 @@ +extern crate libsqlite3_sys as ffi; + +use std::marker::PhantomData; +use std::os::raw as libc; +use std::ptr::NonNull; +use std::{slice, str}; + +use crate::row::*; +use crate::sqlite::{Sqlite, SqliteType}; + +/// Raw sqlite value as received from the database +/// +/// Use existing `FromSql` implementations to convert this into +/// rust values: +#[allow(missing_debug_implementations, missing_copy_implementations)] +pub struct SqliteValue<'a> { + value: NonNull, + p: PhantomData<&'a ()>, +} + +#[derive(Clone)] +pub struct SqliteRow<'a> { + stmt: NonNull, + next_col_index: libc::c_int, + p: PhantomData<&'a ()>, +} + +impl<'a> SqliteValue<'a> { + pub(crate) unsafe fn new(inner: *mut ffi::sqlite3_value) -> Option { + NonNull::new(inner) + .map(|value| SqliteValue { + value, + p: PhantomData, + }) + .and_then(|value| { + // We check here that the actual value represented by the inner + // `sqlite3_value` is not `NULL` (is sql meaning, not ptr meaning) + if value.is_null() { + None + } else { + Some(value) + } + }) + } + + pub(crate) fn read_text(&self) -> &str { + unsafe { + let ptr = ffi::sqlite3_value_text(self.value.as_ptr()); + let len = ffi::sqlite3_value_bytes(self.value.as_ptr()); + let bytes = slice::from_raw_parts(ptr as *const u8, len as usize); + // The string is guaranteed to be utf8 according to + // https://www.sqlite.org/c3ref/value_blob.html + str::from_utf8_unchecked(bytes) + } + } + + pub(crate) fn read_blob(&self) -> &[u8] { + unsafe { + let ptr = ffi::sqlite3_value_blob(self.value.as_ptr()); + let len = ffi::sqlite3_value_bytes(self.value.as_ptr()); + slice::from_raw_parts(ptr as *const u8, len as usize) + } + } + + pub(crate) fn read_integer(&self) -> i32 { + unsafe { ffi::sqlite3_value_int(self.value.as_ptr()) as i32 } + } + + pub(crate) fn read_long(&self) -> i64 { + unsafe { ffi::sqlite3_value_int64(self.value.as_ptr()) as i64 } + } + + pub(crate) fn read_double(&self) -> f64 { + unsafe { ffi::sqlite3_value_double(self.value.as_ptr()) as f64 } + } + + /// Get the type of the value as returned by sqlite + pub fn value_type(&self) -> Option { + let tpe = unsafe { ffi::sqlite3_value_type(self.value.as_ptr()) }; + match tpe { + ffi::SQLITE_TEXT => Some(SqliteType::Text), + ffi::SQLITE_INTEGER => Some(SqliteType::Long), + ffi::SQLITE_FLOAT => Some(SqliteType::Double), + ffi::SQLITE_BLOB => Some(SqliteType::Binary), + ffi::SQLITE_NULL => None, + _ => unreachable!("Sqlite docs saying this is not reachable"), + } + } + + pub(crate) fn is_null(&self) -> bool { + self.value_type().is_none() + } +} + +impl<'a> SqliteRow<'a> { + pub(crate) unsafe fn new(inner_statement: NonNull) -> Self { + SqliteRow { + stmt: inner_statement, + next_col_index: 0, + p: PhantomData, + } + } +} + +impl<'a> Row<'a, Sqlite> for SqliteRow<'a> { + type Field = SqliteField<'a>; + type InnerPartialRow = Self; + + fn field_count(&self) -> usize { + column_count(self.stmt) as usize + } + + fn get(&self, idx: I) -> Option + where + Self: RowIndex, + { + let idx = self.idx(idx)?; + Some(SqliteField { + stmt: self.stmt, + col_idx: idx as i32, + p: PhantomData, + }) + } + + fn partial_row(&self, range: std::ops::Range) -> PartialRow { + PartialRow::new(self, range) + } +} + +impl<'a> RowIndex for SqliteRow<'a> { + fn idx(&self, idx: usize) -> Option { + if idx < self.field_count() { + Some(idx) + } else { + None + } + } +} + +impl<'a, 'b> RowIndex<&'a str> for SqliteRow<'b> { + fn idx(&self, field_name: &'a str) -> Option { + (0..column_count(self.stmt)) + .find(|idx| column_name(self.stmt, *idx) == Some(field_name)) + .map(|a| a as usize) + } +} + +pub struct SqliteField<'a> { + stmt: NonNull, + col_idx: i32, + p: PhantomData<&'a ()>, +} + +impl<'a> Field<'a, Sqlite> for SqliteField<'a> { + fn field_name(&self) -> Option<&'a str> { + column_name(self.stmt, self.col_idx) + } + + fn is_null(&self) -> bool { + self.value().is_none() + } + + fn value(&self) -> Option> { + unsafe { + let ptr = ffi::sqlite3_column_value(self.stmt.as_ptr(), self.col_idx); + SqliteValue::new(ptr) + } + } +} + +fn column_name<'a>(stmt: NonNull, field_number: i32) -> Option<&'a str> { + unsafe { + let ptr = ffi::sqlite3_column_name(stmt.as_ptr(), field_number); + if ptr.is_null() { + None + } else { + Some(std::ffi::CStr::from_ptr(ptr).to_str().expect( + "The Sqlite documentation states that this is UTF8. \ + If you see this error message something has gone \ + horribliy wrong. Please open an issue at the \ + diesel repository.", + )) + } + } +} + +fn column_count(stmt: NonNull) -> i32 { + unsafe { ffi::sqlite3_column_count(stmt.as_ptr()) } +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/connection/statement_iterator.rs b/collector/benchmarks/diesel/diesel/src/sqlite/connection/statement_iterator.rs new file mode 100644 index 000000000..91195631f --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/connection/statement_iterator.rs @@ -0,0 +1,36 @@ +use std::marker::PhantomData; + +use super::stmt::StatementUse; +use crate::deserialize::FromSqlRow; +use crate::result::Error::DeserializationError; +use crate::result::QueryResult; +use crate::sqlite::Sqlite; + +pub struct StatementIterator<'a, ST, T> { + stmt: StatementUse<'a>, + _marker: PhantomData<(ST, T)>, +} + +impl<'a, ST, T> StatementIterator<'a, ST, T> { + pub fn new(stmt: StatementUse<'a>) -> Self { + StatementIterator { + stmt: stmt, + _marker: PhantomData, + } + } +} + +impl<'a, ST, T> Iterator for StatementIterator<'a, ST, T> +where + T: FromSqlRow, +{ + type Item = QueryResult; + + fn next(&mut self) -> Option { + let row = match self.stmt.step() { + Ok(row) => row, + Err(e) => return Some(Err(e)), + }; + row.map(|row| T::build_from_row(&row).map_err(DeserializationError)) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/connection/stmt.rs b/collector/benchmarks/diesel/diesel/src/sqlite/connection/stmt.rs new file mode 100644 index 000000000..3ca28c8ec --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/connection/stmt.rs @@ -0,0 +1,154 @@ +extern crate libsqlite3_sys as ffi; + +use std::ffi::{CStr, CString}; +use std::io::{stderr, Write}; +use std::os::raw as libc; +use std::ptr::{self, NonNull}; + +use super::raw::RawConnection; +use super::serialized_value::SerializedValue; +use super::sqlite_value::SqliteRow; +use crate::result::Error::DatabaseError; +use crate::result::*; +use crate::sqlite::SqliteType; + +pub struct Statement { + inner_statement: NonNull, + bind_index: libc::c_int, +} + +impl Statement { + pub fn prepare(raw_connection: &RawConnection, sql: &str) -> QueryResult { + let mut stmt = ptr::null_mut(); + let mut unused_portion = ptr::null(); + let prepare_result = unsafe { + ffi::sqlite3_prepare_v2( + raw_connection.internal_connection.as_ptr(), + CString::new(sql)?.as_ptr(), + sql.len() as libc::c_int, + &mut stmt, + &mut unused_portion, + ) + }; + + ensure_sqlite_ok(prepare_result, raw_connection.internal_connection.as_ptr()).map(|_| { + Statement { + inner_statement: unsafe { NonNull::new_unchecked(stmt) }, + bind_index: 0, + } + }) + } + + fn run(&mut self) -> QueryResult<()> { + self.step().map(|_| ()) + } + + pub fn bind(&mut self, tpe: SqliteType, value: Option>) -> QueryResult<()> { + self.bind_index += 1; + let value = SerializedValue { + ty: tpe, + data: value, + }; + let result = value.bind_to(self.inner_statement, self.bind_index); + + ensure_sqlite_ok(result, self.raw_connection()) + } + + fn step(&mut self) -> QueryResult> { + unsafe { + match ffi::sqlite3_step(self.inner_statement.as_ptr()) { + ffi::SQLITE_DONE => Ok(None), + ffi::SQLITE_ROW => Ok(Some(SqliteRow::new(self.inner_statement))), + _ => Err(last_error(self.raw_connection())), + } + } + } + + fn reset(&mut self) { + self.bind_index = 0; + unsafe { ffi::sqlite3_reset(self.inner_statement.as_ptr()) }; + } + + fn raw_connection(&self) -> *mut ffi::sqlite3 { + unsafe { ffi::sqlite3_db_handle(self.inner_statement.as_ptr()) } + } +} + +fn ensure_sqlite_ok(code: libc::c_int, raw_connection: *mut ffi::sqlite3) -> QueryResult<()> { + if code == ffi::SQLITE_OK { + Ok(()) + } else { + Err(last_error(raw_connection)) + } +} + +fn last_error(raw_connection: *mut ffi::sqlite3) -> Error { + let error_message = last_error_message(raw_connection); + let error_information = Box::new(error_message); + let error_kind = match last_error_code(raw_connection) { + ffi::SQLITE_CONSTRAINT_UNIQUE | ffi::SQLITE_CONSTRAINT_PRIMARYKEY => { + DatabaseErrorKind::UniqueViolation + } + ffi::SQLITE_CONSTRAINT_FOREIGNKEY => DatabaseErrorKind::ForeignKeyViolation, + ffi::SQLITE_CONSTRAINT_NOTNULL => DatabaseErrorKind::NotNullViolation, + ffi::SQLITE_CONSTRAINT_CHECK => DatabaseErrorKind::CheckViolation, + _ => DatabaseErrorKind::__Unknown, + }; + DatabaseError(error_kind, error_information) +} + +fn last_error_message(conn: *mut ffi::sqlite3) -> String { + let c_str = unsafe { CStr::from_ptr(ffi::sqlite3_errmsg(conn)) }; + c_str.to_string_lossy().into_owned() +} + +fn last_error_code(conn: *mut ffi::sqlite3) -> libc::c_int { + unsafe { ffi::sqlite3_extended_errcode(conn) } +} + +impl Drop for Statement { + fn drop(&mut self) { + use std::thread::panicking; + + let raw_connection = self.raw_connection(); + let finalize_result = unsafe { ffi::sqlite3_finalize(self.inner_statement.as_ptr()) }; + if let Err(e) = ensure_sqlite_ok(finalize_result, raw_connection) { + if panicking() { + write!( + stderr(), + "Error finalizing SQLite prepared statement: {:?}", + e + ) + .expect("Error writing to `stderr`"); + } else { + panic!("Error finalizing SQLite prepared statement: {:?}", e); + } + } + } +} + +pub struct StatementUse<'a> { + statement: &'a mut Statement, +} + +impl<'a> StatementUse<'a> { + pub fn new(statement: &'a mut Statement) -> Self { + StatementUse { + statement: statement, + } + } + + pub fn run(&mut self) -> QueryResult<()> { + self.statement.run() + } + + pub fn step(&mut self) -> QueryResult> { + self.statement.step() + } +} + +impl<'a> Drop for StatementUse<'a> { + fn drop(&mut self) { + self.statement.reset(); + } +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/mod.rs b/collector/benchmarks/diesel/diesel/src/sqlite/mod.rs new file mode 100644 index 000000000..931785b7e --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/mod.rs @@ -0,0 +1,35 @@ +//! Provides types and functions related to working with SQLite +//! +//! Much of this module is re-exported from database agnostic locations. +//! However, if you are writing code specifically to extend Diesel on +//! SQLite, you may need to work with this module directly. + +mod backend; +mod connection; +mod types; + +pub mod query_builder; + +pub use self::backend::{Sqlite, SqliteType}; +pub use self::connection::SqliteConnection; +pub use self::connection::SqliteValue; +pub use self::query_builder::SqliteQueryBuilder; + +/// Trait for the implementation of a SQLite aggregate function +/// +/// This trait is to be used in conjunction with the `sql_function!` +/// macro for defining a custom SQLite aggregate function. See +/// the documentation [there](../prelude/macro.sql_function.html) for details. +pub trait SqliteAggregateFunction: Default { + /// The result type of the SQLite aggregate function + type Output; + + /// The `step()` method is called once for every record of the query + fn step(&mut self, args: Args); + + /// After the last row has been processed, the `finalize()` method is + /// called to compute the result of the aggregate function. If no rows + /// were processed `aggregator` will be `None` and `finalize()` can be + /// used to specify a default result + fn finalize(aggregator: Option) -> Self::Output; +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/query_builder/limit_offset.rs b/collector/benchmarks/diesel/diesel/src/sqlite/query_builder/limit_offset.rs new file mode 100644 index 000000000..8d713a3ec --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/query_builder/limit_offset.rs @@ -0,0 +1,126 @@ +use crate::query_builder::limit_clause::{LimitClause, NoLimitClause}; +use crate::query_builder::limit_offset_clause::{BoxedLimitOffsetClause, LimitOffsetClause}; +use crate::query_builder::offset_clause::{NoOffsetClause, OffsetClause}; +use crate::query_builder::{AstPass, IntoBoxedClause, QueryFragment}; +use crate::result::QueryResult; +use crate::sqlite::Sqlite; + +impl QueryFragment for LimitOffsetClause { + fn walk_ast(&self, _out: AstPass) -> QueryResult<()> { + Ok(()) + } +} + +impl QueryFragment for LimitOffsetClause, NoOffsetClause> +where + LimitClause: QueryFragment, +{ + fn walk_ast(&self, out: AstPass) -> QueryResult<()> { + self.limit_clause.walk_ast(out)?; + Ok(()) + } +} + +impl QueryFragment for LimitOffsetClause> +where + OffsetClause: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + // Sqlite requires a limit clause in front of any offset clause + // using `LIMIT -1` is the same as not having any limit clause + // https://sqlite.org/lang_select.html + out.push_sql(" LIMIT -1 "); + self.offset_clause.walk_ast(out)?; + Ok(()) + } +} + +impl QueryFragment for LimitOffsetClause, OffsetClause> +where + LimitClause: QueryFragment, + OffsetClause: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.limit_clause.walk_ast(out.reborrow())?; + self.offset_clause.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl<'a> QueryFragment for BoxedLimitOffsetClause<'a, Sqlite> { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + match (self.limit.as_ref(), self.offset.as_ref()) { + (Some(limit), Some(offset)) => { + limit.walk_ast(out.reborrow())?; + offset.walk_ast(out.reborrow())?; + } + (Some(limit), None) => { + limit.walk_ast(out.reborrow())?; + } + (None, Some(offset)) => { + // See the `QueryFragment` implementation for `LimitOffsetClause` for details. + out.push_sql(" LIMIT -1 "); + offset.walk_ast(out.reborrow())?; + } + (None, None) => {} + } + Ok(()) + } +} + +// Have explicit impls here because we need to set `Some`/`None` for the clauses +// correspondingly, otherwise we cannot match on it in the `QueryFragment` impl +// above +impl<'a> IntoBoxedClause<'a, Sqlite> for LimitOffsetClause { + type BoxedClause = BoxedLimitOffsetClause<'a, Sqlite>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: None, + offset: None, + } + } +} + +impl<'a, L> IntoBoxedClause<'a, Sqlite> for LimitOffsetClause, NoOffsetClause> +where + L: QueryFragment + Send + 'a, +{ + type BoxedClause = BoxedLimitOffsetClause<'a, Sqlite>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: Some(Box::new(self.limit_clause)), + offset: None, + } + } +} + +impl<'a, O> IntoBoxedClause<'a, Sqlite> for LimitOffsetClause> +where + O: QueryFragment + Send + 'a, +{ + type BoxedClause = BoxedLimitOffsetClause<'a, Sqlite>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: None, + offset: Some(Box::new(self.offset_clause)), + } + } +} + +impl<'a, L, O> IntoBoxedClause<'a, Sqlite> for LimitOffsetClause, OffsetClause> +where + L: QueryFragment + Send + 'a, + O: QueryFragment + Send + 'a, +{ + type BoxedClause = BoxedLimitOffsetClause<'a, Sqlite>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: Some(Box::new(self.limit_clause)), + offset: Some(Box::new(self.offset_clause)), + } + } +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/query_builder/mod.rs b/collector/benchmarks/diesel/diesel/src/sqlite/query_builder/mod.rs new file mode 100644 index 000000000..546c96344 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/query_builder/mod.rs @@ -0,0 +1,42 @@ +//! The SQLite query builder + +use super::backend::Sqlite; +use crate::query_builder::QueryBuilder; +use crate::result::QueryResult; + +mod limit_offset; + +/// Constructs SQL queries for use with the SQLite backend +#[allow(missing_debug_implementations)] +#[derive(Default)] +pub struct SqliteQueryBuilder { + sql: String, +} + +impl SqliteQueryBuilder { + /// Construct a new query builder with an empty query + pub fn new() -> Self { + SqliteQueryBuilder::default() + } +} + +impl QueryBuilder for SqliteQueryBuilder { + fn push_sql(&mut self, sql: &str) { + self.sql.push_str(sql); + } + + fn push_identifier(&mut self, identifier: &str) -> QueryResult<()> { + self.push_sql("`"); + self.push_sql(&identifier.replace("`", "``")); + self.push_sql("`"); + Ok(()) + } + + fn push_bind_param(&mut self) { + self.push_sql("?"); + } + + fn finish(self) -> String { + self.sql + } +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/types/date_and_time/chrono.rs b/collector/benchmarks/diesel/diesel/src/sqlite/types/date_and_time/chrono.rs new file mode 100644 index 000000000..7d186c6f6 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/types/date_and_time/chrono.rs @@ -0,0 +1,378 @@ +extern crate chrono; + +use self::chrono::{NaiveDate, NaiveDateTime, NaiveTime}; +use std::io::Write; + +use crate::backend; +use crate::deserialize::{self, FromSql}; +use crate::serialize::{self, Output, ToSql}; +use crate::sql_types::{Date, Text, Time, Timestamp}; +use crate::sqlite::Sqlite; + +const SQLITE_DATE_FORMAT: &str = "%F"; + +impl FromSql for NaiveDate { + fn from_sql(value: backend::RawValue) -> deserialize::Result { + let text_ptr = <*const str as FromSql>::from_sql(value)?; + let text = unsafe { &*text_ptr }; + Self::parse_from_str(text, SQLITE_DATE_FORMAT).map_err(Into::into) + } +} + +impl ToSql for NaiveDate { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let s = self.format(SQLITE_DATE_FORMAT).to_string(); + ToSql::::to_sql(&s, out) + } +} + +impl FromSql for NaiveTime { + fn from_sql(value: backend::RawValue) -> deserialize::Result { + let text_ptr = <*const str as FromSql>::from_sql(value)?; + let text = unsafe { &*text_ptr }; + let valid_time_formats = &[ + // Most likely + "%T%.f", // All other valid formats in order of documentation + "%R", "%RZ", "%T%.fZ", "%R%:z", "%T%.f%:z", + ]; + + for format in valid_time_formats { + if let Ok(time) = Self::parse_from_str(text, format) { + return Ok(time); + } + } + + Err(format!("Invalid time {}", text).into()) + } +} + +impl ToSql for NaiveTime { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let s = self.format("%T%.f").to_string(); + ToSql::::to_sql(&s, out) + } +} + +impl FromSql for NaiveDateTime { + fn from_sql(value: backend::RawValue) -> deserialize::Result { + let text_ptr = <*const str as FromSql>::from_sql(value)?; + let text = unsafe { &*text_ptr }; + + let sqlite_datetime_formats = &[ + // Most likely format + "%F %T%.f", + // Other formats in order of appearance in docs + "%F %R", + "%F %RZ", + "%F %R%:z", + "%F %T%.fZ", + "%F %T%.f%:z", + "%FT%R", + "%FT%RZ", + "%FT%R%:z", + "%FT%T%.f", + "%FT%T%.fZ", + "%FT%T%.f%:z", + ]; + + for format in sqlite_datetime_formats { + if let Ok(dt) = Self::parse_from_str(text, format) { + return Ok(dt); + } + } + + if let Ok(julian_days) = text.parse::() { + let epoch_in_julian_days = 2_440_587.5; + let seconds_in_day = 86400.0; + let timestamp = (julian_days - epoch_in_julian_days) * seconds_in_day; + let seconds = timestamp as i64; + let nanos = (timestamp.fract() * 1E9) as u32; + if let Some(timestamp) = Self::from_timestamp_opt(seconds, nanos) { + return Ok(timestamp); + } + } + + Err(format!("Invalid datetime {}", text).into()) + } +} + +impl ToSql for NaiveDateTime { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let s = self.format("%F %T%.f").to_string(); + ToSql::::to_sql(&s, out) + } +} + +#[cfg(test)] +mod tests { + extern crate chrono; + extern crate dotenv; + + use self::chrono::{Duration, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc}; + use self::dotenv::dotenv; + + use crate::dsl::{now, sql}; + use crate::prelude::*; + use crate::select; + use crate::sql_types::{Text, Time, Timestamp}; + + sql_function!(fn datetime(x: Text) -> Timestamp); + sql_function!(fn time(x: Text) -> Time); + sql_function!(fn date(x: Text) -> Date); + + fn connection() -> SqliteConnection { + dotenv().ok(); + + let connection_url = ::std::env::var("SQLITE_DATABASE_URL") + .or_else(|_| ::std::env::var("DATABASE_URL")) + .expect("DATABASE_URL must be set in order to run tests"); + SqliteConnection::establish(&connection_url).unwrap() + } + + #[test] + fn unix_epoch_encodes_correctly() { + let connection = connection(); + let time = NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0); + let query = select(datetime("1970-01-01 00:00:00.000000").eq(time)); + assert_eq!(Ok(true), query.get_result(&connection)); + } + + #[test] + fn unix_epoch_decodes_correctly_in_all_possible_formats() { + let connection = connection(); + let time = NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0); + let valid_epoch_formats = vec![ + "1970-01-01 00:00", + "1970-01-01 00:00:00", + "1970-01-01 00:00:00.000", + "1970-01-01 00:00:00.000000", + "1970-01-01T00:00", + "1970-01-01T00:00:00", + "1970-01-01T00:00:00.000", + "1970-01-01T00:00:00.000000", + "1970-01-01 00:00Z", + "1970-01-01 00:00:00Z", + "1970-01-01 00:00:00.000Z", + "1970-01-01 00:00:00.000000Z", + "1970-01-01T00:00Z", + "1970-01-01T00:00:00Z", + "1970-01-01T00:00:00.000Z", + "1970-01-01T00:00:00.000000Z", + "1970-01-01 00:00+00:00", + "1970-01-01 00:00:00+00:00", + "1970-01-01 00:00:00.000+00:00", + "1970-01-01 00:00:00.000000+00:00", + "1970-01-01T00:00+00:00", + "1970-01-01T00:00:00+00:00", + "1970-01-01T00:00:00.000+00:00", + "1970-01-01T00:00:00.000000+00:00", + "1970-01-01 00:00+01:00", + "1970-01-01 00:00:00+01:00", + "1970-01-01 00:00:00.000+01:00", + "1970-01-01 00:00:00.000000+01:00", + "1970-01-01T00:00+01:00", + "1970-01-01T00:00:00+01:00", + "1970-01-01T00:00:00.000+01:00", + "1970-01-01T00:00:00.000000+01:00", + "1970-01-01T00:00-01:00", + "1970-01-01T00:00:00-01:00", + "1970-01-01T00:00:00.000-01:00", + "1970-01-01T00:00:00.000000-01:00", + "1970-01-01T00:00-01:00", + "1970-01-01T00:00:00-01:00", + "1970-01-01T00:00:00.000-01:00", + "1970-01-01T00:00:00.000000-01:00", + "2440587.5", + ]; + + for s in valid_epoch_formats { + let epoch_from_sql = + select(sql::(&format!("'{}'", s))).get_result(&connection); + assert_eq!(Ok(time), epoch_from_sql, "format {} failed", s); + } + } + + #[test] + fn times_relative_to_now_encode_correctly() { + let connection = connection(); + let time = Utc::now().naive_utc() + Duration::seconds(60); + let query = select(now.lt(time)); + assert_eq!(Ok(true), query.get_result(&connection)); + + let time = Utc::now().naive_utc() - Duration::seconds(600); + let query = select(now.gt(time)); + assert_eq!(Ok(true), query.get_result(&connection)); + } + + #[test] + fn times_of_day_encode_correctly() { + let connection = connection(); + + let midnight = NaiveTime::from_hms(0, 0, 0); + let query = select(time("00:00:00.000000").eq(midnight)); + assert!(query.get_result::(&connection).unwrap()); + + let noon = NaiveTime::from_hms(12, 0, 0); + let query = select(time("12:00:00.000000").eq(noon)); + assert!(query.get_result::(&connection).unwrap()); + + let roughly_half_past_eleven = NaiveTime::from_hms_micro(23, 37, 4, 2200); + let query = select(sql::

,)+ + { + type Table = Tab; + + fn walk_ast<__DB: Backend>(&self, mut out: AstPass<__DB>) -> QueryResult<()> { + $( + if $idx != 0 { + out.push_sql(", "); + } + self.$idx.walk_ast(out.reborrow())?; + )+ + Ok(()) + } + } + + impl<$($T: QueryId),+> QueryId for ($($T,)+) { + type QueryId = ($($T::QueryId,)+); + + const HAS_STATIC_QUERY_ID: bool = $($T::HAS_STATIC_QUERY_ID &&)+ true; + } + + const _: () = { + #[derive(ValidGrouping)] + #[diesel(foreign_derive)] + struct TupleWrapper<$($T,)*>(($($T,)*)); + }; + + impl_valid_grouping_for_tuple_of_columns!($($T,)*); + + impl<$($T,)+ Tab> UndecoratedInsertRecord for ($($T,)+) + where + $($T: UndecoratedInsertRecord,)+ + { + } + + impl<$($T,)+ __DB> CanInsertInSingleQuery<__DB> for ($($T,)+) + where + __DB: Backend, + $($T: CanInsertInSingleQuery<__DB>,)+ + { + fn rows_to_insert(&self) -> Option { + $(debug_assert_eq!(self.$idx.rows_to_insert(), Some(1));)+ + Some(1) + } + } + + impl<$($T,)+ $($ST,)+ Tab> Insertable for ($($T,)+) + where + $($T: Insertable>,)+ + { + type Values = ValuesClause<($($ST,)+), Tab>; + + fn values(self) -> Self::Values { + ValuesClause::new(($(self.$idx.values().values,)+)) + } + } + + impl<'a, $($T,)+ Tab> Insertable for &'a ($($T,)+) + where + ($(&'a $T,)+): Insertable, + { + type Values = <($(&'a $T,)+) as Insertable>::Values; + + fn values(self) -> Self::Values { + ($(&self.$idx,)+).values() + } + } + + #[allow(unused_assignments)] + impl<$($T,)+ Tab, __DB> InsertValues for ($($T,)+) + where + Tab: Table, + __DB: Backend, + $($T: InsertValues,)+ + { + fn column_names(&self, mut out: AstPass<__DB>) -> QueryResult<()> { + let mut needs_comma = false; + $( + let noop_element = self.$idx.is_noop()?; + if !noop_element { + if needs_comma { + out.push_sql(", "); + } + self.$idx.column_names(out.reborrow())?; + needs_comma = true; + } + )+ + Ok(()) + } + } + + impl<$($T,)+ QS> SelectableExpression for ($($T,)+) where + $($T: SelectableExpression,)+ + ($($T,)+): AppearsOnTable, + { + } + + impl<$($T,)+ QS> AppearsOnTable for ($($T,)+) where + $($T: AppearsOnTable,)+ + ($($T,)+): Expression, + { + } + + impl AsChangeset for ($($T,)+) where + $($T: AsChangeset,)+ + Target: QuerySource, + { + type Target = Target; + type Changeset = ($($T::Changeset,)+); + + fn as_changeset(self) -> Self::Changeset { + ($(self.$idx.as_changeset(),)+) + } + } + + impl<$($T,)+ Parent> BelongsTo for ($($T,)+) where + A: BelongsTo, + { + type ForeignKey = A::ForeignKey; + type ForeignKeyColumn = A::ForeignKeyColumn; + + fn foreign_key(&self) -> Option<&Self::ForeignKey> { + self.0.foreign_key() + } + + fn foreign_key_column() -> Self::ForeignKeyColumn { + A::foreign_key_column() + } + } + + impl<$($T,)+ Next> TupleAppend for ($($T,)+) { + type Output = ($($T,)+ Next); + + #[allow(non_snake_case)] + fn tuple_append(self, next: Next) -> Self::Output { + let ($($T,)+) = self; + ($($T,)+ next) + } + } + + impl<$($T,)+ ST> AsExpressionList for ($($T,)+) where + $($T: AsExpression,)+ + ST: SqlType + TypedExpressionType, + { + type Expression = ($($T::Expression,)+); + + fn as_expression_list(self) -> Self::Expression { + ($(self.$idx.as_expression(),)+) + } + } + + impl_sql_type!($($T,)*); + + impl<$($T,)* __DB, $($ST,)*> Queryable<($($ST,)*), __DB> for ($($T,)*) + where __DB: Backend, + Self: FromStaticSqlRow<($($ST,)*), __DB>, + { + type Row = Self; + + fn build(row: Self::Row) -> Self { + row + } + } + + impl<__T, $($ST,)* __DB> FromStaticSqlRow, __DB> for Option<__T> where + __DB: Backend, + ($($ST,)*): SqlType, + __T: FromSqlRow<($($ST,)*), __DB>, + { + + #[allow(non_snake_case, unused_variables, unused_mut)] + fn build_from_row<'a>(row: &impl Row<'a, __DB>) + -> deserialize::Result + { + match <__T as FromSqlRow<($($ST,)*), __DB>>::build_from_row(row) { + Ok(v) => Ok(Some(v)), + Err(e) if e.is::() => Ok(None), + Err(e) => Err(e) + } + } + } + + impl<__T, __DB, $($ST,)*> Queryable, __DB> for Option<__T> + where __DB: Backend, + Self: FromStaticSqlRow, __DB>, + ($($ST,)*): SqlType, + { + type Row = Self; + + fn build(row: Self::Row) -> Self { + row + } + } + + impl<$($T,)*> TupleSize for ($($T,)*) + where $($T: TupleSize,)* + { + const SIZE: usize = $($T::SIZE +)* 0; + } + + impl<$($T,)*> TupleSize for Nullable<($($T,)*)> + where $($T: TupleSize,)* + ($($T,)*): SqlType, + { + const SIZE: usize = $($T::SIZE +)* 0; + } + + impl<$($T,)* __DB> QueryMetadata<($($T,)*)> for __DB + where __DB: Backend, + $(__DB: QueryMetadata<$T>,)* + { + fn row_metadata(lookup: &Self::MetadataLookup, row: &mut Vec>) { + $( + <__DB as QueryMetadata<$T>>::row_metadata(lookup, row); + )* + } + } + + impl<$($T,)* __DB> QueryMetadata> for __DB + where __DB: Backend, + $(__DB: QueryMetadata<$T>,)* + { + fn row_metadata(lookup: &Self::MetadataLookup, row: &mut Vec>) { + $( + <__DB as QueryMetadata<$T>>::row_metadata(lookup, row); + )* + } + } + + impl<$($T,)* __DB> deserialize::QueryableByName< __DB> for ($($T,)*) + where __DB: Backend, + $($T: deserialize::QueryableByName<__DB>,)* + { + fn build<'a>(row: &impl NamedRow<'a, __DB>) -> deserialize::Result { + Ok(($( + <$T as deserialize::QueryableByName<__DB>>::build(row)?, + )*)) + } + } + + )+ + } +} + +macro_rules! impl_from_sql_row { + (($T1: ident,), ($ST1: ident,)) => { + impl<$T1, $ST1, __DB> crate::deserialize::FromStaticSqlRow<($ST1,), __DB> for ($T1,) where + __DB: Backend, + $T1: FromSqlRow<$ST1, __DB>, + { + + #[allow(non_snake_case, unused_variables, unused_mut)] + fn build_from_row<'a>(row: &impl Row<'a, __DB>) + -> deserialize::Result + { + Ok(($T1::build_from_row(row)?,)) + } + } + }; + (($T1: ident, $($T: ident,)*), ($ST1: ident, $($ST: ident,)*)) => { + impl<$T1, $($T,)* $($ST,)* __DB> FromSqlRow<($($ST,)* crate::sql_types::Untyped), __DB> for ($($T,)* $T1) + where __DB: Backend, + $T1: FromSqlRow, + $( + $T: FromSqlRow<$ST, __DB> + StaticallySizedRow<$ST, __DB>, + )* + { + #[allow(non_snake_case, unused_variables, unused_mut)] + fn build_from_row<'a>(full_row: &impl Row<'a, __DB>) + -> deserialize::Result + { + let field_count = full_row.field_count(); + + let mut static_field_count = 0; + $( + let row = full_row.partial_row(static_field_count..static_field_count + $T::FIELD_COUNT); + static_field_count += $T::FIELD_COUNT; + let $T = $T::build_from_row(&row)?; + )* + + let row = full_row.partial_row(static_field_count..field_count); + + Ok(($($T,)* $T1::build_from_row(&row)?,)) + } + } + + impl<$T1, $ST1, $($T,)* $($ST,)* __DB> FromStaticSqlRow<($($ST,)* $ST1,), __DB> for ($($T,)* $T1,) where + __DB: Backend, + $T1: FromSqlRow<$ST1, __DB>, + $( + $T: FromSqlRow<$ST, __DB> + StaticallySizedRow<$ST, __DB>, + )* + + { + + #[allow(non_snake_case, unused_variables, unused_mut)] + fn build_from_row<'a>(full_row: &impl Row<'a, __DB>) + -> deserialize::Result + { + let field_count = full_row.field_count(); + + let mut static_field_count = 0; + $( + let row = full_row.partial_row(static_field_count..static_field_count + $T::FIELD_COUNT); + static_field_count += $T::FIELD_COUNT; + let $T = $T::build_from_row(&row)?; + )* + + let row = full_row.partial_row(static_field_count..field_count); + + Ok(($($T,)* $T1::build_from_row(&row)?,)) + } + } + } +} + +macro_rules! impl_valid_grouping_for_tuple_of_columns { + ( + @build + start_ts = [$($ST: ident,)*], + ts = [$T1: ident,], + bounds = [$($bounds: tt)*], + is_aggregate = [$($is_aggregate: tt)*], + ) => { + impl<$($ST,)* Col> IsContainedInGroupByfor ($($ST,)*) + where Col: Column, + $($ST: IsContainedInGroupBy,)* + $($bounds)* + <$T1 as IsContainedInGroupBy>::Output: is_contained_in_group_by::IsAny<$($is_aggregate)*>, + { + type Output = <<$T1 as IsContainedInGroupBy>::Output as is_contained_in_group_by::IsAny<$($is_aggregate)*>>::Output; + } + }; + ( + @build + start_ts = [$($ST: ident,)*], + ts = [$T1: ident, $($T: ident,)+], + bounds = [$($bounds: tt)*], + is_aggregate = [$($is_aggregate: tt)*], + ) => { + impl_valid_grouping_for_tuple_of_columns! { + @build + start_ts = [$($ST,)*], + ts = [$($T,)*], + bounds = [ + $($bounds)* + <$T1 as IsContainedInGroupBy>::Output: is_contained_in_group_by::IsAny<$($is_aggregate)*>, + ], + is_aggregate = [ + <<$T1 as IsContainedInGroupBy>::Output as is_contained_in_group_by::IsAny<$($is_aggregate)*>>::Output + ], + } + }; + ($T1: ident, $($T: ident,)+) => { + impl_valid_grouping_for_tuple_of_columns! { + @build + start_ts = [$T1, $($T,)*], + ts = [$($T,)*], + bounds = [], + is_aggregate = [<$T1 as IsContainedInGroupBy>::Output], + } + }; + ($T1: ident,) => { + impl<$T1, Col> IsContainedInGroupByfor ($T1,) + where Col: Column, + $T1: IsContainedInGroupBy+ { + type Output = <$T1 as IsContainedInGroupBy>::Output; + } + }; +} + +macro_rules! impl_sql_type { + ( + @build + start_ts = [$($ST: ident,)*], + ts = [$T1: ident,], + bounds = [$($bounds: tt)*], + is_null = [$($is_null: tt)*], + )=> { + impl<$($ST,)*> SqlType for ($($ST,)*) + where + $($ST: SqlType,)* + $($bounds)* + $T1::IsNull: OneIsNullable<$($is_null)*>, + { + type IsNull = <$T1::IsNull as OneIsNullable<$($is_null)*>>::Out; + } + + }; + ( + @build + start_ts = [$($ST: ident,)*], + ts = [$T1: ident, $($T: ident,)+], + bounds = [$($bounds: tt)*], + is_null = [$($is_null: tt)*], + )=> { + impl_sql_type!{ + @build + start_ts = [$($ST,)*], + ts = [$($T,)*], + bounds = [$($bounds)* $T1::IsNull: OneIsNullable<$($is_null)*>,], + is_null = [<$T1::IsNull as OneIsNullable<$($is_null)*>>::Out], + } + }; + ($T1: ident, $($T: ident,)+) => { + impl_sql_type!{ + @build + start_ts = [$T1, $($T,)*], + ts = [$($T,)*], + bounds = [], + is_null = [$T1::IsNull], + } + }; + ($T1: ident,) => { + impl<$T1> SqlType for ($T1,) + where $T1: SqlType, + { + type IsNull = $T1::IsNull; + } + } +} + +__diesel_for_each_tuple!(tuple_impls); diff --git a/collector/benchmarks/diesel/diesel/src/upsert/mod.rs b/collector/benchmarks/diesel/diesel/src/upsert/mod.rs new file mode 100644 index 000000000..dc073ee70 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/upsert/mod.rs @@ -0,0 +1,27 @@ +//! Types and functions related to PG's and Sqlite's `ON CONFLICT` clause +//! +//! Upsert is currently supported by diesel for the following database systems: +//! +//! * PostgreSQL version 9.5 or newer +//! * Sqlite3 version 3.24.0 or newer +//! +//! See [the methods on `InsertStatement`](../query_builder/struct.InsertStatement.html#impl-2) +//! for usage examples. +//! +//! Constructing an upsert statement from an existing select statement +//! requires a where clause on sqlite due to a ambiguity in their +//! parser. See [the corresponding documentation](https://www.sqlite.org/lang_UPSERT.html) +//! for details. +use crate::query_builder::upsert::on_conflict_actions::Excluded; + +mod on_conflict_extension; + +pub use self::on_conflict_extension::DecoratableTarget; +pub use self::on_conflict_extension::*; +#[cfg(feature = "postgres")] +pub use crate::pg::query_builder::on_constraint::*; + +/// Represents `excluded.column` in an `ON CONFLICT DO UPDATE` clause. +pub fn excluded(excluded: T) -> Excluded { + Excluded::new(excluded) +} diff --git a/collector/benchmarks/diesel/diesel/src/upsert/on_conflict_docs_setup.rs b/collector/benchmarks/diesel/diesel/src/upsert/on_conflict_docs_setup.rs new file mode 100644 index 000000000..fa9206013 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/upsert/on_conflict_docs_setup.rs @@ -0,0 +1,9 @@ +include!("../doctest_setup.rs"); +use crate::schema::users; + +#[derive(Clone, Copy, Insertable, AsChangeset)] +#[table_name="users"] +struct User<'a> { + id: i32, + name: &'a str, +} diff --git a/collector/benchmarks/diesel/diesel/src/upsert/on_conflict_extension.rs b/collector/benchmarks/diesel/diesel/src/upsert/on_conflict_extension.rs new file mode 100644 index 000000000..50937a6e4 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/upsert/on_conflict_extension.rs @@ -0,0 +1,391 @@ +use crate::expression::Expression; +use crate::query_builder::upsert::into_conflict_clause::IntoConflictValueClause; +use crate::query_builder::upsert::on_conflict_actions::*; +use crate::query_builder::upsert::on_conflict_clause::*; +use crate::query_builder::upsert::on_conflict_target::*; +pub use crate::query_builder::upsert::on_conflict_target_decorations::DecoratableTarget; +use crate::query_builder::{AsChangeset, InsertStatement, UndecoratedInsertRecord}; +use crate::query_source::QuerySource; +use crate::sql_types::BoolOrNullableBool; + +impl InsertStatement +where + U: UndecoratedInsertRecord + IntoConflictValueClause, +{ + /// Adds `ON CONFLICT DO NOTHING` to the insert statement, without + /// specifying any columns or constraints to restrict the conflict to. + /// + /// # Examples + /// + /// ### Single Record + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// # + /// # #[cfg(any(feature = "sqlite", feature = "postgres"))] + /// # fn main() { + /// # use self::users::dsl::*; + /// # let conn = establish_connection(); + /// # #[cfg(feature = "postgres")] + /// # conn.execute("TRUNCATE TABLE users").unwrap(); + /// # #[cfg(feature = "sqlite")] + /// # conn.execute("DELETE FROM users").unwrap(); + /// let user = User { id: 1, name: "Sean", }; + /// + /// let inserted_row_count = diesel::insert_into(users) + /// .values(&user) + /// .on_conflict_do_nothing() + /// .execute(&conn); + /// assert_eq!(Ok(1), inserted_row_count); + /// + /// let inserted_row_count = diesel::insert_into(users) + /// .values(&user) + /// .on_conflict_do_nothing() + /// .execute(&conn); + /// assert_eq!(Ok(0), inserted_row_count); + /// # } + /// # #[cfg(feature = "mysql")] + /// # fn main() {} + /// ``` + /// + /// ### Vec of Records + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// # + /// # #[cfg(any(feature = "sqlite", feature = "postgres"))] + /// # fn main() { + /// # use self::users::dsl::*; + /// # let conn = establish_connection(); + /// # #[cfg(feature = "postgres")] + /// # conn.execute("TRUNCATE TABLE users").unwrap(); + /// # #[cfg(feature = "postgres")] + /// let user = User { id: 1, name: "Sean", }; + /// + /// # #[cfg(feature = "postgres")] + /// let inserted_row_count = diesel::insert_into(users) + /// .values(&vec![user, user]) + /// .on_conflict_do_nothing() + /// .execute(&conn); + /// # #[cfg(feature = "postgres")] + /// assert_eq!(Ok(1), inserted_row_count); + /// # } + /// # #[cfg(feature = "mysql")] + /// # fn main() {} + /// ``` + pub fn on_conflict_do_nothing( + self, + ) -> InsertStatement, Op, Ret> + { + self.replace_values(|values| OnConflictValues::do_nothing(values.into_value_clause())) + } + + /// Adds an `ON CONFLICT` to the insert statement, if a conflict occurs + /// for the given unique constraint. + /// + /// `Target` can be one of: + /// + /// - A column + /// - A tuple of columns + /// - [`on_constraint("constraint_name")`][`on_constraint`] + /// + /// # Examples + /// + /// ### Specifying a column as the target + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// # + /// # #[cfg(any(feature = "sqlite", feature = "postgres"))] + /// # fn main() { + /// # use self::users::dsl::*; + /// # let conn = establish_connection(); + /// # #[cfg(feature = "postgres")] + /// # conn.execute("TRUNCATE TABLE users").unwrap(); + /// # #[cfg(feature = "sqlite")] + /// # conn.execute("DELETE FROM users").unwrap(); + /// conn.execute("CREATE UNIQUE INDEX users_name ON users (name)").unwrap(); + /// let user = User { id: 1, name: "Sean", }; + /// let same_name_different_id = User { id: 2, name: "Sean" }; + /// let same_id_different_name = User { id: 1, name: "Pascal" }; + /// + /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn)); + /// + /// let inserted_row_count = diesel::insert_into(users) + /// .values(&same_name_different_id) + /// .on_conflict(name) + /// .do_nothing() + /// .execute(&conn); + /// assert_eq!(Ok(0), inserted_row_count); + /// + /// let pk_conflict_result = diesel::insert_into(users) + /// .values(&same_id_different_name) + /// .on_conflict(name) + /// .do_nothing() + /// .execute(&conn); + /// assert!(pk_conflict_result.is_err()); + /// # } + /// # #[cfg(feature = "mysql")] + /// # fn main() {} + /// ``` + /// + /// ### Specifying multiple columns as the target + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # hair_color -> VarChar, + /// # } + /// # } + /// # + /// # #[derive(Clone, Copy, Insertable)] + /// # #[table_name="users"] + /// # struct User<'a> { + /// # id: i32, + /// # name: &'a str, + /// # hair_color: &'a str, + /// # } + /// # + /// # #[cfg(any(feature = "sqlite", feature = "postgres"))] + /// # fn main() { + /// # use self::users::dsl::*; + /// use diesel::upsert::*; + /// + /// # let conn = establish_connection(); + /// # conn.execute("DROP TABLE users").unwrap(); + /// # conn.execute("CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT, hair_color TEXT)").unwrap(); + /// conn.execute("CREATE UNIQUE INDEX users_name_hair_color ON users (name, hair_color)").unwrap(); + /// let user = User { id: 1, name: "Sean", hair_color: "black" }; + /// let same_name_different_hair_color = User { id: 2, name: "Sean", hair_color: "brown" }; + /// let same_name_same_hair_color = User { id: 3, name: "Sean", hair_color: "black" }; + /// + /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn)); + /// + /// let inserted_row_count = diesel::insert_into(users) + /// .values(&same_name_different_hair_color) + /// .on_conflict((name, hair_color)) + /// .do_nothing() + /// .execute(&conn); + /// assert_eq!(Ok(1), inserted_row_count); + /// + /// let inserted_row_count = diesel::insert_into(users) + /// .values(&same_name_same_hair_color) + /// .on_conflict((name, hair_color)) + /// .do_nothing() + /// .execute(&conn); + /// assert_eq!(Ok(0), inserted_row_count); + /// # } + /// # #[cfg(feature = "mysql")] + /// # fn main() {} + /// ``` + /// + /// See the documentation for [`on_constraint`] and [`do_update`] for + /// more examples. + /// + /// [`on_constraint`]: ../upsert/fn.on_constraint.html + /// [`do_update`]: ../upsert/struct.IncompleteOnConflict.html#method.do_update + pub fn on_conflict( + self, + target: Target, + ) -> IncompleteOnConflict, ConflictTarget> + where + ConflictTarget: OnConflictTarget, + { + IncompleteOnConflict { + stmt: self.replace_values(IntoConflictValueClause::into_value_clause), + target: ConflictTarget(target), + } + } +} + +impl DecoratableTarget

for IncompleteOnConflict +where + P: Expression, + P::SqlType: BoolOrNullableBool, + T: DecoratableTarget

, +{ + type FilterOutput = IncompleteOnConflict>::FilterOutput>; + fn filter_target(self, predicate: P) -> Self::FilterOutput { + IncompleteOnConflict { + stmt: self.stmt, + target: self.target.filter_target(predicate), + } + } +} + +/// A partially constructed `ON CONFLICT` clause. +#[derive(Debug, Clone, Copy)] +pub struct IncompleteOnConflict { + stmt: Stmt, + target: Target, +} + +impl IncompleteOnConflict, Target> { + /// Creates a query with `ON CONFLICT (target) DO NOTHING` + /// + /// If you want to do nothing when *any* constraint conflicts, use + /// [`on_conflict_do_nothing`] instead. See [`on_conflict`] for usage + /// examples. + /// + /// [`on_conflict_do_nothing`]: ../../query_builder/struct.InsertStatement.html#method.on_conflict_do_nothing + /// [`on_conflict`]: ../../query_builder/struct.InsertStatement.html#method.on_conflict + pub fn do_nothing(self) -> InsertStatement, Op, Ret> { + let target = self.target; + self.stmt + .replace_values(|values| OnConflictValues::new(values, target, DoNothing)) + } +} + +impl IncompleteOnConflict { + /// Used to create a query in the form `ON CONFLICT (...) DO UPDATE ...` + /// + /// Call `.set` on the result of this function with the changes you want to + /// apply. The argument to `set` can be anything that implements `AsChangeset` + /// (e.g. anything you could pass to `set` on a normal update statement). + /// + /// Note: When inserting more than one row at a time, this query can still fail + /// if the rows being inserted conflict with each other. + /// + /// # Examples + /// + /// ## Set specific value on conflict + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// # + /// # #[cfg(any(feature = "sqlite", feature = "postgres"))] + /// # fn main() { + /// # use self::users::dsl::*; + /// # let conn = establish_connection(); + /// # #[cfg(feature = "postgres")] + /// # conn.execute("TRUNCATE TABLE users").unwrap(); + /// # #[cfg(feature = "sqlite")] + /// # conn.execute("DELETE FROM users").unwrap(); + /// let user = User { id: 1, name: "Pascal" }; + /// let user2 = User { id: 1, name: "Sean" }; + /// + /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn)); + /// + /// let insert_count = diesel::insert_into(users) + /// .values(&user2) + /// .on_conflict(id) + /// .do_update() + /// .set(name.eq("I DONT KNOW ANYMORE")) + /// .execute(&conn); + /// assert_eq!(Ok(1), insert_count); + /// + /// let users_in_db = users.load(&conn); + /// assert_eq!(Ok(vec![(1, "I DONT KNOW ANYMORE".to_string())]), users_in_db); + /// # } + /// # #[cfg(feature = "mysql")] + /// # fn main() {} + /// ``` + /// + /// ## Set `AsChangeset` struct on conflict + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// # + /// # #[cfg(any(feature = "sqlite", feature = "postgres"))] + /// # fn main() { + /// # use self::users::dsl::*; + /// # let conn = establish_connection(); + /// # #[cfg(feature = "postgres")] + /// # conn.execute("TRUNCATE TABLE users").unwrap(); + /// # #[cfg(feature = "sqlite")] + /// # conn.execute("DELETE FROM users").unwrap(); + /// let user = User { id: 1, name: "Pascal" }; + /// let user2 = User { id: 1, name: "Sean" }; + /// + /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn)); + /// + /// let insert_count = diesel::insert_into(users) + /// .values(&user2) + /// .on_conflict(id) + /// .do_update() + /// .set(&user2) + /// .execute(&conn); + /// assert_eq!(Ok(1), insert_count); + /// + /// let users_in_db = users.load(&conn); + /// assert_eq!(Ok(vec![(1, "Sean".to_string())]), users_in_db); + /// # } + /// # #[cfg(feature = "mysql")] + /// # fn main() {} + /// ``` + /// + /// ## Use `excluded` to get the rejected value + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// # + /// # #[cfg(any(feature = "sqlite", feature = "postgres"))] + /// # fn main() { + /// # use self::users::dsl::*; + /// use diesel::upsert::excluded; + /// + /// # let conn = establish_connection(); + /// # #[cfg(feature = "postgres")] + /// # conn.execute("TRUNCATE TABLE users").unwrap(); + /// let user = User { id: 1, name: "Pascal" }; + /// let user2 = User { id: 1, name: "Sean" }; + /// let user3 = User { id: 2, name: "Tess" }; + /// + /// # #[cfg(feature = "postgres")] + /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn)); + /// + /// #[cfg(feature = "postgres")] + /// let insert_count = diesel::insert_into(users) + /// .values(&vec![user2, user3]) + /// .on_conflict(id) + /// .do_update() + /// .set(name.eq(excluded(name))) + /// .execute(&conn); + /// # #[cfg(feature = "postgres")] + /// assert_eq!(Ok(2), insert_count); + /// + /// # #[cfg(feature = "postgres")] + /// let users_in_db = users.load(&conn); + /// # #[cfg(feature = "postgres")] + /// assert_eq!(Ok(vec![(1, "Sean".to_string()), (2, "Tess".to_string())]), users_in_db); + /// # } + /// # #[cfg(feature = "mysql")] + /// # fn main() {} + /// ``` + pub fn do_update(self) -> IncompleteDoUpdate { + IncompleteDoUpdate { + stmt: self.stmt, + target: self.target, + } + } +} + +/// A partially constructed `ON CONFLICT DO UPDATE` clause. +#[derive(Debug, Clone, Copy)] +pub struct IncompleteDoUpdate { + stmt: Stmt, + target: Target, +} + +impl IncompleteDoUpdate, Target> { + /// See [`do_update`] for usage examples. + /// + /// [`do_update`]: struct.IncompleteOnConflict.html#method.do_update + pub fn set( + self, + changes: Changes, + ) -> InsertStatement>, Op, Ret> + where + T: QuerySource, + Changes: AsChangeset, + { + let target = self.target; + self.stmt.replace_values(|values| { + OnConflictValues::new(values, target, DoUpdate::new(changes.as_changeset())) + }) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/util.rs b/collector/benchmarks/diesel/diesel/src/util.rs new file mode 100644 index 000000000..ed0439d49 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/util.rs @@ -0,0 +1,11 @@ +/// Treats tuples as a list which can be appended to. e.g. +/// `(a,).tuple_append(b) == (a, b)` +pub trait TupleAppend { + type Output; + + fn tuple_append(self, right: T) -> Self::Output; +} + +pub trait TupleSize { + const SIZE: usize; +} diff --git a/collector/benchmarks/diesel/diesel_derives/Cargo.toml b/collector/benchmarks/diesel/diesel_derives/Cargo.toml new file mode 100644 index 000000000..601aefbf4 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "diesel_derives" +version = "2.0.0" +authors = ["Sean Griffin "] +license = "MIT OR Apache-2.0" +description = "You should not use this crate directly, it is internal to Diesel." +documentation = "https://diesel.rs/guides/" +homepage = "https://diesel.rs" +repository = "https://github.com/diesel-rs/diesel/tree/master/diesel_derives" +autotests = false +include = ["src/**/*", "LICENSE-*"] + +[dependencies] +syn = { version = "1.0.1", features = ["full", "fold"] } +quote = "1" +proc-macro2 = "1" + +[dev-dependencies] +cfg-if = "0.1.10" +dotenv = "0.15" + +[dev-dependencies.diesel] +version = "~2.0.0" +path = "../diesel" + +[lib] +proc-macro = true + +[[test]] +name = "tests" + +[features] +default = [] +nightly = ["proc-macro2/nightly"] +postgres = [] +sqlite = [] +mysql = [] diff --git a/collector/benchmarks/diesel/diesel_derives/LICENSE-APACHE b/collector/benchmarks/diesel/diesel_derives/LICENSE-APACHE new file mode 120000 index 000000000..965b606f3 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/collector/benchmarks/diesel/diesel_derives/LICENSE-MIT b/collector/benchmarks/diesel/diesel_derives/LICENSE-MIT new file mode 120000 index 000000000..76219eb72 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/collector/benchmarks/diesel/diesel_derives/src/as_changeset.rs b/collector/benchmarks/diesel/diesel_derives/src/as_changeset.rs new file mode 100644 index 000000000..4b8dec211 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/as_changeset.rs @@ -0,0 +1,122 @@ +use proc_macro2; +use proc_macro2::Span; +use syn; + +use diagnostic_shim::*; +use field::*; +use meta::*; +use model::*; +use util::*; + +pub fn derive(item: syn::DeriveInput) -> Result { + let treat_none_as_null = MetaItem::with_name(&item.attrs, "changeset_options") + .map(|meta| { + meta.warn_if_other_options(&["treat_none_as_null"]); + meta.required_nested_item("treat_none_as_null") + .map(|m| m.expect_bool_value()) + }) + .unwrap_or(Ok(false))?; + let model = Model::from_item(&item)?; + let struct_name = &model.name; + let table_name = model.table_name(); + + let (_, ty_generics, where_clause) = item.generics.split_for_impl(); + let mut impl_generics = item.generics.clone(); + impl_generics.params.push(parse_quote!('update)); + let (impl_generics, _, _) = impl_generics.split_for_impl(); + + let fields_for_update = model + .fields() + .iter() + .filter(|f| !model.primary_key_names.contains(&f.column_name())) + .collect::>(); + let ref_changeset_ty = fields_for_update.iter().map(|field| { + field_changeset_ty( + field, + &table_name, + treat_none_as_null, + Some(quote!(&'update)), + ) + }); + let ref_changeset_expr = fields_for_update + .iter() + .map(|field| field_changeset_expr(field, &table_name, treat_none_as_null, Some(quote!(&)))); + let direct_changeset_ty = fields_for_update + .iter() + .map(|field| field_changeset_ty(field, &table_name, treat_none_as_null, None)); + let direct_changeset_expr = fields_for_update + .iter() + .map(|field| field_changeset_expr(field, &table_name, treat_none_as_null, None)); + + if fields_for_update.is_empty() { + Span::call_site() + .error( + "Deriving `AsChangeset` on a structure that only contains the primary key isn't supported." + ) + .help("If you want to change the primary key of a row, you should do so with `.set(table::id.eq(new_id))`.") + .note("`#[derive(AsChangeset)]` never changes the primary key of a row.") + .emit(); + } + + Ok(wrap_in_dummy_mod(quote!( + use diesel::query_builder::AsChangeset; + use diesel::prelude::*; + + impl #impl_generics AsChangeset for &'update #struct_name #ty_generics + #where_clause + { + type Target = #table_name::table; + type Changeset = <(#(#ref_changeset_ty,)*) as AsChangeset>::Changeset; + + fn as_changeset(self) -> Self::Changeset { + (#(#ref_changeset_expr,)*).as_changeset() + } + } + + impl #impl_generics AsChangeset for #struct_name #ty_generics + #where_clause + { + type Target = #table_name::table; + type Changeset = <(#(#direct_changeset_ty,)*) as AsChangeset>::Changeset; + + fn as_changeset(self) -> Self::Changeset { + (#(#direct_changeset_expr,)*).as_changeset() + } + } + ))) +} + +fn field_changeset_ty( + field: &Field, + table_name: &syn::Ident, + treat_none_as_null: bool, + lifetime: Option, +) -> syn::Type { + let column_name = field.column_name(); + if !treat_none_as_null && is_option_ty(&field.ty) { + let field_ty = inner_of_option_ty(&field.ty); + parse_quote!(std::option::Option>) + } else { + let field_ty = &field.ty; + parse_quote!(diesel::dsl::Eq<#table_name::#column_name, #lifetime #field_ty>) + } +} + +fn field_changeset_expr( + field: &Field, + table_name: &syn::Ident, + treat_none_as_null: bool, + lifetime: Option, +) -> syn::Expr { + let field_access = field.name.access(); + let column_name = field.column_name(); + if !treat_none_as_null && is_option_ty(&field.ty) { + if lifetime.is_some() { + parse_quote!(self#field_access.as_ref().map(|x| #table_name::#column_name.eq(x))) + } else { + parse_quote!(self#field_access.map(|x| #table_name::#column_name.eq(x))) + } + } else { + parse_quote!(#table_name::#column_name.eq(#lifetime self#field_access)) + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/as_expression.rs b/collector/benchmarks/diesel/diesel_derives/src/as_expression.rs new file mode 100644 index 000000000..427819e80 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/as_expression.rs @@ -0,0 +1,115 @@ +use proc_macro2; +use syn; + +use meta::*; +use util::*; + +pub fn derive(item: syn::DeriveInput) -> Result { + let flags = + MetaItem::with_name(&item.attrs, "diesel").unwrap_or_else(|| MetaItem::empty("diesel")); + let is_sized = !flags.has_flag("not_sized"); + + let sql_types = MetaItem::all_with_name(&item.attrs, "sql_type"); + let any_sql_types = !sql_types.is_empty(); + let sql_types = sql_types + .into_iter() + .filter_map(|attr| attr.ty_value().map_err(Diagnostic::emit).ok()); + + let (impl_generics, ..) = item.generics.split_for_impl(); + let lifetimes = item.generics.lifetimes().collect::>(); + let ty_params = item.generics.type_params().collect::>(); + let struct_ty = ty_for_foreign_derive(&item, &flags)?; + + let tokens = sql_types.map(|sql_type| { + let lifetimes = &lifetimes; + let ty_params = &ty_params; + let tokens = quote!( + impl<'expr, #(#lifetimes,)* #(#ty_params,)*> AsExpression<#sql_type> + for &'expr #struct_ty + { + type Expression = Bound<#sql_type, Self>; + + fn as_expression(self) -> Self::Expression { + Bound::new(self) + } + } + + impl<'expr, #(#lifetimes,)* #(#ty_params,)*> AsExpression> + for &'expr #struct_ty + { + type Expression = Bound, Self>; + + fn as_expression(self) -> Self::Expression { + Bound::new(self) + } + } + + impl<'expr2, 'expr, #(#lifetimes,)* #(#ty_params,)*> AsExpression<#sql_type> + for &'expr2 &'expr #struct_ty + { + type Expression = Bound<#sql_type, Self>; + + fn as_expression(self) -> Self::Expression { + Bound::new(self) + } + } + + impl<'expr2, 'expr, #(#lifetimes,)* #(#ty_params,)*> AsExpression> + for &'expr2 &'expr #struct_ty + { + type Expression = Bound, Self>; + + fn as_expression(self) -> Self::Expression { + Bound::new(self) + } + } + + impl<#(#lifetimes,)* #(#ty_params,)* __DB> diesel::serialize::ToSql, __DB> + for #struct_ty + where + __DB: diesel::backend::Backend, + Self: ToSql<#sql_type, __DB>, + { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + ToSql::<#sql_type, __DB>::to_sql(self, out) + } + } + ); + if is_sized { + quote!( + #tokens + + impl#impl_generics AsExpression<#sql_type> for #struct_ty { + type Expression = Bound<#sql_type, Self>; + + fn as_expression(self) -> Self::Expression { + Bound::new(self) + } + } + + impl#impl_generics AsExpression> for #struct_ty { + type Expression = Bound, Self>; + + fn as_expression(self) -> Self::Expression { + Bound::new(self) + } + } + ) + } else { + tokens + } + }); + + if any_sql_types { + Ok(wrap_in_dummy_mod(quote! { + use diesel::expression::AsExpression; + use diesel::expression::bound::Bound; + use diesel::sql_types::Nullable; + use diesel::serialize::{self, ToSql, Output}; + + #(#tokens)* + })) + } else { + Ok(quote!()) + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/associations.rs b/collector/benchmarks/diesel/diesel_derives/src/associations.rs new file mode 100644 index 000000000..ea529fa5e --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/associations.rs @@ -0,0 +1,206 @@ +use proc_macro2; +use proc_macro2::Span; +use syn; +use syn::fold::Fold; +use syn::spanned::Spanned; + +use diagnostic_shim::*; +use meta::*; +use model::*; +use util::*; + +pub fn derive(item: syn::DeriveInput) -> Result { + let model = Model::from_item(&item)?; + let tokens = MetaItem::all_with_name(&item.attrs, "belongs_to") + .into_iter() + .filter_map( + |attr| match derive_belongs_to(&model, &item.generics, attr) { + Ok(t) => Some(t), + Err(e) => { + e.emit(); + None + } + }, + ); + + Ok(wrap_in_dummy_mod(quote!(#(#tokens)*))) +} + +fn derive_belongs_to( + model: &Model, + generics: &syn::Generics, + meta: MetaItem, +) -> Result { + let AssociationOptions { + parent_struct, + foreign_key, + } = AssociationOptions::from_meta(meta)?; + let (_, ty_generics, _) = generics.split_for_impl(); + + let foreign_key_field = model.find_column(&foreign_key)?; + let struct_name = &model.name; + let foreign_key_access = foreign_key_field.name.access(); + let foreign_key_ty = &foreign_key_field.ty; + let table_name = model.table_name(); + + let mut generics = generics.clone(); + + let parent_struct = ReplacePathLifetimes::new(|i, span| { + let letter = char::from(b'b' + i as u8); + let lifetime = syn::Lifetime::new(&format!("'__{}", letter), span); + generics.params.push(parse_quote!(#lifetime)); + lifetime + }) + .fold_type_path(parent_struct); + + generics.params.push(parse_quote!(__FK)); + { + let where_clause = generics.where_clause.get_or_insert(parse_quote!(where)); + where_clause + .predicates + .push(parse_quote!(__FK: std::hash::Hash + std::cmp::Eq)); + where_clause.predicates.push( + parse_quote!(for<'__a> &'__a #foreign_key_ty: std::convert::Into<::std::option::Option<&'__a __FK>>), + ); + where_clause.predicates.push( + parse_quote!(for<'__a> &'__a #parent_struct: diesel::associations::Identifiable), + ); + } + + let foreign_key_expr = quote!(std::convert::Into::into(&self#foreign_key_access)); + let foreign_key_ty = quote!(__FK); + + let (impl_generics, _, where_clause) = generics.split_for_impl(); + + Ok(quote! { + impl #impl_generics diesel::associations::BelongsTo<#parent_struct> + for #struct_name #ty_generics + #where_clause + { + type ForeignKey = #foreign_key_ty; + type ForeignKeyColumn = #table_name::#foreign_key; + + fn foreign_key(&self) -> std::option::Option<&Self::ForeignKey> { + #foreign_key_expr + } + + fn foreign_key_column() -> Self::ForeignKeyColumn { + #table_name::#foreign_key + } + } + }) +} + +struct AssociationOptions { + parent_struct: syn::TypePath, + foreign_key: syn::Ident, +} + +impl AssociationOptions { + fn from_meta(meta: MetaItem) -> Result { + let parent_struct = meta + .nested()? + .find(|m| m.path().is_ok() || m.name().is_ident("parent")) + .ok_or_else(|| meta.span()) + .and_then(|m| { + m.path() + .map(|i| parse_quote!(#i)) + .or_else(|_| m.ty_value()) + .map_err(|_| m.span()) + }) + .and_then(|ty| match ty { + syn::Type::Path(ty_path) => Ok(ty_path), + _ => Err(ty.span()), + }) + .map_err(|span| { + span.error("Expected a struct name") + .help("e.g. `#[belongs_to(User)]` or `#[belongs_to(parent = \"User<'_>\")]") + })?; + let foreign_key = { + let parent_struct_name = parent_struct + .path + .segments + .last() + .expect("paths always have at least one segment"); + meta.nested_item("foreign_key")? + .map(|i| i.ident_value()) + .unwrap_or_else(|| Ok(infer_foreign_key(&parent_struct_name.ident)))? + }; + + let (unrecognized_paths, unrecognized_options): (Vec<_>, Vec<_>) = meta + .nested()? + .skip(1) + .filter(|n| !n.name().is_ident("foreign_key")) + .partition(|item| item.path().is_ok()); + + if !unrecognized_paths.is_empty() { + let parent_path_string = path_to_string(&parent_struct.path); + let unrecognized_path_strings: Vec<_> = unrecognized_paths + .iter() + .filter_map(|item| item.path().as_ref().map(path_to_string).ok()) + .collect(); + + meta.span() + .warning(format!( + "belongs_to takes a single parent. Change\n\ + \tbelongs_to({}, {})\n\ + to\n\ + \tbelongs_to({})\n\ + {}", + parent_path_string, + unrecognized_path_strings.join(","), + parent_path_string, + unrecognized_path_strings + .iter() + .map(|path| format!("\tbelongs_to({})", path)) + .collect::>() + .join("\n") + )) + .emit(); + } + + for ignored in unrecognized_options { + ignored + .span() + .warning(format!( + "Unrecognized option {}", + path_to_string(&ignored.name()) + )) + .emit(); + } + + Ok(Self { + parent_struct, + foreign_key, + }) + } +} + +fn infer_foreign_key(name: &syn::Ident) -> syn::Ident { + let snake_case = camel_to_snake(&name.to_string()); + syn::Ident::new(&format!("{}_id", snake_case), name.span()) +} + +struct ReplacePathLifetimes { + count: usize, + f: F, +} + +impl ReplacePathLifetimes { + fn new(f: F) -> Self { + Self { count: 0, f } + } +} + +impl Fold for ReplacePathLifetimes +where + F: FnMut(usize, Span) -> syn::Lifetime, +{ + fn fold_lifetime(&mut self, mut lt: syn::Lifetime) -> syn::Lifetime { + if lt.ident == "_" { + lt = (self.f)(self.count, lt.span()); + self.count += 1; + } + lt + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/diagnostic_shim.rs b/collector/benchmarks/diesel/diesel_derives/src/diagnostic_shim.rs new file mode 100644 index 000000000..8392f8088 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/diagnostic_shim.rs @@ -0,0 +1,87 @@ +use proc_macro2::Span; + +pub trait EmitErrorExt { + fn emit_error(self) -> Option; +} + +impl EmitErrorExt for Result { + fn emit_error(self) -> Option { + self.map_err(Diagnostic::emit).ok() + } +} + +pub trait DiagnosticShim { + fn error>(self, msg: T) -> Diagnostic; + fn warning>(self, msg: T) -> Diagnostic; +} + +#[cfg(feature = "nightly")] +impl DiagnosticShim for Span { + fn error>(self, msg: T) -> Diagnostic { + self.unstable().error(msg) + } + + fn warning>(self, msg: T) -> Diagnostic { + self.unstable().warning(msg) + } +} + +#[cfg(not(feature = "nightly"))] +impl DiagnosticShim for Span { + fn error>(self, msg: T) -> Diagnostic { + Diagnostic::error(msg) + } + + fn warning>(self, msg: T) -> Diagnostic { + Diagnostic::warning(msg) + } +} + +#[cfg(feature = "nightly")] +pub use proc_macro::Diagnostic; + +#[cfg(not(feature = "nightly"))] +pub struct Diagnostic { + message: String, + level: Level, +} + +#[cfg(not(feature = "nightly"))] +impl Diagnostic { + fn error>(msg: T) -> Self { + Diagnostic { + message: msg.into(), + level: Level::Error, + } + } + + fn warning>(msg: T) -> Self { + Diagnostic { + message: msg.into(), + level: Level::Warning, + } + } + + pub fn help>(mut self, msg: T) -> Self { + self.message.push_str("\n"); + self.message.push_str(&msg.into()); + self + } + + pub fn note(self, msg: &str) -> Self { + self.help(msg) + } + + pub fn emit(self) { + match self.level { + Level::Error => panic!("{}", self.message), + Level::Warning => println!("{}", self.message), + } + } +} + +#[cfg(not(feature = "nightly"))] +enum Level { + Warning, + Error, +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/diesel_numeric_ops.rs b/collector/benchmarks/diesel/diesel_derives/src/diesel_numeric_ops.rs new file mode 100644 index 000000000..b8e021990 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/diesel_numeric_ops.rs @@ -0,0 +1,83 @@ +use proc_macro2; +use syn; + +use util::*; + +pub fn derive(mut item: syn::DeriveInput) -> Result { + let struct_name = &item.ident; + + { + let where_clause = item + .generics + .where_clause + .get_or_insert(parse_quote!(where)); + where_clause.predicates.push(parse_quote!(Self: Expression)); + where_clause.predicates.push_punct(Default::default()); + } + let (_, ty_generics, where_clause) = item.generics.split_for_impl(); + let mut impl_generics = item.generics.clone(); + impl_generics.params.push(parse_quote!(__Rhs)); + let (impl_generics, _, _) = impl_generics.split_for_impl(); + + Ok(wrap_in_dummy_mod(quote! { + use diesel::expression::{ops, Expression, AsExpression}; + use diesel::sql_types::ops::{Add, Sub, Mul, Div}; + use diesel::sql_types::{SqlType, SingleValue}; + + impl #impl_generics ::std::ops::Add<__Rhs> for #struct_name #ty_generics + #where_clause + Self: Expression, + ::SqlType: Add, + <::SqlType as Add>::Rhs: SqlType + SingleValue, + __Rhs: AsExpression<<::SqlType as Add>::Rhs>, + { + type Output = ops::Add; + + fn add(self, rhs: __Rhs) -> Self::Output { + ops::Add::new(self, rhs.as_expression()) + } + } + + impl #impl_generics ::std::ops::Sub<__Rhs> for #struct_name #ty_generics + #where_clause + Self: Expression, + ::SqlType: Sub, + <::SqlType as Sub>::Rhs: SqlType + SingleValue, + __Rhs: AsExpression<<::SqlType as Sub>::Rhs>, + { + type Output = ops::Sub; + + fn sub(self, rhs: __Rhs) -> Self::Output { + ops::Sub::new(self, rhs.as_expression()) + } + } + + impl #impl_generics ::std::ops::Mul<__Rhs> for #struct_name #ty_generics + #where_clause + Self: Expression, + ::SqlType: Mul, + <::SqlType as Mul>::Rhs: SqlType + SingleValue, + __Rhs: AsExpression<<::SqlType as Mul>::Rhs>, + { + type Output = ops::Mul; + + fn mul(self, rhs: __Rhs) -> Self::Output { + ops::Mul::new(self, rhs.as_expression()) + } + } + + impl #impl_generics ::std::ops::Div<__Rhs> for #struct_name #ty_generics + #where_clause + Self: Expression, + ::SqlType: Div, + <::SqlType as Div>::Rhs: SqlType + SingleValue, + __Rhs: AsExpression<<::SqlType as Div>::Rhs>, + { + type Output = ops::Div; + + fn div(self, rhs: __Rhs) -> Self::Output { + ops::Div::new(self, rhs.as_expression()) + } + } + })) +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/field.rs b/collector/benchmarks/diesel/diesel_derives/src/field.rs new file mode 100644 index 000000000..0e1efe25e --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/field.rs @@ -0,0 +1,125 @@ +use proc_macro2::{self, Ident, Span}; +use quote::ToTokens; +use std::borrow::Cow; +use syn; +use syn::spanned::Spanned; + +use meta::*; +use util::*; + +pub struct Field { + pub ty: syn::Type, + pub name: FieldName, + pub span: Span, + pub sql_type: Option, + pub flags: MetaItem, + column_name_from_attribute: Option, +} + +impl Field { + pub fn from_struct_field(field: &syn::Field, index: usize) -> Self { + let column_name_from_attribute = + MetaItem::with_name(&field.attrs, "column_name").map(|m| m.expect_ident_value()); + let name = match field.ident.clone() { + Some(mut x) => { + // https://github.com/rust-lang/rust/issues/47983#issuecomment-362817105 + let span = x.span(); + x.set_span(fix_span(span, Span::call_site())); + FieldName::Named(x) + } + None => FieldName::Unnamed(syn::Index { + index: index as u32, + // https://github.com/rust-lang/rust/issues/47312 + span: Span::call_site(), + }), + }; + let sql_type = MetaItem::with_name(&field.attrs, "sql_type") + .and_then(|m| m.ty_value().map_err(Diagnostic::emit).ok()); + let flags = MetaItem::with_name(&field.attrs, "diesel") + .unwrap_or_else(|| MetaItem::empty("diesel")); + let span = field.span(); + + Self { + ty: field.ty.clone(), + column_name_from_attribute, + name, + sql_type, + flags, + span, + } + } + + pub fn column_name(&self) -> syn::Ident { + self.column_name_from_attribute + .clone() + .unwrap_or_else(|| match self.name { + FieldName::Named(ref x) => x.clone(), + _ => { + self.span + .error( + "All fields of tuple structs must be annotated with `#[column_name]`", + ) + .emit(); + Ident::new("unknown_column", self.span) + } + }) + } + + pub fn has_flag(&self, flag: &str) -> bool { + self.flags.has_flag(flag) + } + + pub fn ty_for_serialize(&self) -> Result, Diagnostic> { + if let Some(meta) = self.flags.nested_item("serialize_as")? { + let ty = meta.ty_value()?; + Ok(Some(ty)) + } else { + Ok(None) + } + } + + pub fn ty_for_deserialize(&self) -> Result, Diagnostic> { + if let Some(meta) = self.flags.nested_item("deserialize_as")? { + meta.ty_value().map(Cow::Owned) + } else { + Ok(Cow::Borrowed(&self.ty)) + } + } +} + +pub enum FieldName { + Named(syn::Ident), + Unnamed(syn::Index), +} + +impl FieldName { + pub fn assign(&self, expr: syn::Expr) -> syn::FieldValue { + let span = self.span(); + // Parens are to work around https://github.com/rust-lang/rust/issues/47311 + let tokens = quote_spanned!(span=> #self: (#expr)); + parse_quote!(#tokens) + } + + pub fn access(&self) -> proc_macro2::TokenStream { + let span = self.span(); + // Span of the dot is important due to + // https://github.com/rust-lang/rust/issues/47312 + quote_spanned!(span=> .#self) + } + + pub fn span(&self) -> Span { + match *self { + FieldName::Named(ref x) => x.span(), + FieldName::Unnamed(ref x) => x.span, + } + } +} + +impl ToTokens for FieldName { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + match *self { + FieldName::Named(ref x) => x.to_tokens(tokens), + FieldName::Unnamed(ref x) => x.to_tokens(tokens), + } + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/from_sql_row.rs b/collector/benchmarks/diesel/diesel_derives/src/from_sql_row.rs new file mode 100644 index 000000000..c7edb9b7d --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/from_sql_row.rs @@ -0,0 +1,44 @@ +use proc_macro2::*; +use syn; + +use meta::*; +use util::*; + +pub fn derive(mut item: syn::DeriveInput) -> Result { + let flags = + MetaItem::with_name(&item.attrs, "diesel").unwrap_or_else(|| MetaItem::empty("diesel")); + let struct_ty = ty_for_foreign_derive(&item, &flags)?; + + item.generics.params.push(parse_quote!(__ST)); + item.generics.params.push(parse_quote!(__DB)); + { + let where_clause = item + .generics + .where_clause + .get_or_insert(parse_quote!(where)); + where_clause + .predicates + .push(parse_quote!(__DB: diesel::backend::Backend)); + where_clause + .predicates + .push(parse_quote!(__ST: diesel::sql_types::SingleValue)); + where_clause + .predicates + .push(parse_quote!(Self: FromSql<__ST, __DB>)); + } + let (impl_generics, _, where_clause) = item.generics.split_for_impl(); + + Ok(wrap_in_dummy_mod(quote! { + use diesel::deserialize::{FromSql, Queryable}; + + impl #impl_generics Queryable<__ST, __DB> for #struct_ty + #where_clause + { + type Row = Self; + + fn build(row: Self::Row) -> Self { + row + } + } + })) +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/identifiable.rs b/collector/benchmarks/diesel/diesel_derives/src/identifiable.rs new file mode 100644 index 000000000..eb43fa816 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/identifiable.rs @@ -0,0 +1,47 @@ +use proc_macro2; +use syn; + +use model::*; +use util::*; + +pub fn derive(item: syn::DeriveInput) -> Result { + let model = Model::from_item(&item)?; + let struct_name = &model.name; + let table_name = model.table_name(); + + let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); + let mut ref_generics = item.generics.clone(); + ref_generics.params.push(parse_quote!('ident)); + let (ref_generics, ..) = ref_generics.split_for_impl(); + + let (field_ty, field_access): (Vec<_>, Vec<_>) = model + .primary_key_names + .iter() + .filter_map(|pk| model.find_column(pk).emit_error()) + .map(|f| (&f.ty, f.name.access())) + .unzip(); + + Ok(wrap_in_dummy_mod(quote! { + use diesel::associations::{HasTable, Identifiable}; + + impl #impl_generics HasTable for #struct_name #ty_generics + #where_clause + { + type Table = #table_name::table; + + fn table() -> Self::Table { + #table_name::table + } + } + + impl #ref_generics Identifiable for &'ident #struct_name #ty_generics + #where_clause + { + type Id = (#(&'ident #field_ty),*); + + fn id(self) -> Self::Id { + (#(&self#field_access),*) + } + } + })) +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/insertable.rs b/collector/benchmarks/diesel/diesel_derives/src/insertable.rs new file mode 100644 index 000000000..d6a7374f1 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/insertable.rs @@ -0,0 +1,183 @@ +use proc_macro2; +use proc_macro2::Span; +use syn; + +use field::*; +use model::*; +use util::*; + +pub fn derive(item: syn::DeriveInput) -> Result { + let model = Model::from_item(&item)?; + + if model.fields().is_empty() { + return Err(Span::call_site() + .error("Cannot derive Insertable for unit structs") + .help(format!( + "Use `insert_into({}::table).default_values()` if you want `DEFAULT VALUES`", + model.table_name() + ))); + } + + let table_name = &model.table_name(); + let struct_name = &item.ident; + + let (_, ty_generics, where_clause) = item.generics.split_for_impl(); + let mut impl_generics = item.generics.clone(); + impl_generics.params.push(parse_quote!('insert)); + let (impl_generics, ..) = impl_generics.split_for_impl(); + + let mut generate_borrowed_insert = true; + + let mut direct_field_ty = Vec::with_capacity(model.fields().len()); + let mut direct_field_assign = Vec::with_capacity(model.fields().len()); + let mut ref_field_ty = Vec::with_capacity(model.fields().len()); + let mut ref_field_assign = Vec::with_capacity(model.fields().len()); + + for field in model.fields() { + let serialize_as = field.ty_for_serialize()?; + let embed = field.has_flag("embed"); + + match (serialize_as, embed) { + (None, true) => { + direct_field_ty.push(field_ty_embed(field, None)); + direct_field_assign.push(field_expr_embed(field, None)); + ref_field_ty.push(field_ty_embed(field, Some(quote!(&'insert)))); + ref_field_assign.push(field_expr_embed(field, Some(quote!(&)))); + } + (None, false) => { + direct_field_ty.push(field_ty(field, table_name, None)); + direct_field_assign.push(field_expr(field, table_name, None)); + ref_field_ty.push(field_ty(field, table_name, Some(quote!(&'insert)))); + ref_field_assign.push(field_expr(field, table_name, Some(quote!(&)))); + } + (Some(ty), false) => { + direct_field_ty.push(field_ty_serialize_as(field, table_name, &ty)); + direct_field_assign.push(field_expr_serialize_as(field, table_name, &ty)); + + generate_borrowed_insert = false; // as soon as we hit one field with #[diesel(serialize_as)] there is no point in generating the impl of Insertable for borrowed structs + } + (Some(_), true) => { + return Err(field + .flags + .span() + .error("`#[diesel(embed)]` cannot be combined with `#[diesel(serialize_as)]`")) + } + } + } + + let insert_owned = quote! { + impl #impl_generics Insertable<#table_name::table> for #struct_name #ty_generics + #where_clause + { + type Values = <(#(#direct_field_ty,)*) as Insertable<#table_name::table>>::Values; + + fn values(self) -> Self::Values { + (#(#direct_field_assign,)*).values() + } + } + }; + + let insert_borrowed = if generate_borrowed_insert { + quote! { + impl #impl_generics Insertable<#table_name::table> + for &'insert #struct_name #ty_generics + #where_clause + { + type Values = <(#(#ref_field_ty,)*) as Insertable<#table_name::table>>::Values; + + fn values(self) -> Self::Values { + (#(#ref_field_assign,)*).values() + } + } + + impl #impl_generics UndecoratedInsertRecord<#table_name::table> + for #struct_name #ty_generics + #where_clause + { + } + } + } else { + quote! {} + }; + + Ok(wrap_in_dummy_mod(quote! { + use diesel::insertable::Insertable; + use diesel::query_builder::UndecoratedInsertRecord; + use diesel::prelude::*; + + #insert_owned + + #insert_borrowed + })) +} + +fn field_ty_embed(field: &Field, lifetime: Option) -> syn::Type { + let field_ty = &field.ty; + + parse_quote!(#lifetime #field_ty) +} + +fn field_expr_embed(field: &Field, lifetime: Option) -> syn::Expr { + let field_access = field.name.access(); + + parse_quote!(#lifetime self#field_access) +} + +fn field_ty_serialize_as(field: &Field, table_name: &syn::Ident, ty: &syn::Type) -> syn::Type { + let inner_ty = inner_of_option_ty(&ty); + let column_name = field.column_name(); + + parse_quote!( + std::option::Option> + ) +} + +fn field_expr_serialize_as(field: &Field, table_name: &syn::Ident, ty: &syn::Type) -> syn::Expr { + let field_access = field.name.access(); + let column_name = field.column_name(); + let column: syn::Expr = parse_quote!(#table_name::#column_name); + + if is_option_ty(&ty) { + parse_quote!(self#field_access.map(|x| #column.eq(::std::convert::Into::<#ty>::into(x)))) + } else { + parse_quote!(std::option::Option::Some(#column.eq(::std::convert::Into::<#ty>::into(self#field_access)))) + } +} + +fn field_ty( + field: &Field, + table_name: &syn::Ident, + lifetime: Option, +) -> syn::Type { + let inner_ty = inner_of_option_ty(&field.ty); + let column_name = field.column_name(); + + parse_quote!( + std::option::Option> + ) +} + +fn field_expr( + field: &Field, + table_name: &syn::Ident, + lifetime: Option, +) -> syn::Expr { + let field_access = field.name.access(); + let column_name = field.column_name(); + let column: syn::Expr = parse_quote!(#table_name::#column_name); + if is_option_ty(&field.ty) { + if lifetime.is_some() { + parse_quote!(self#field_access.as_ref().map(|x| #column.eq(x))) + } else { + parse_quote!(self#field_access.map(|x| #column.eq(x))) + } + } else { + parse_quote!(std::option::Option::Some(#column.eq(#lifetime self#field_access))) + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/lib.rs b/collector/benchmarks/diesel/diesel_derives/src/lib.rs new file mode 100644 index 000000000..0e4b60ed0 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/lib.rs @@ -0,0 +1,1175 @@ +#![recursion_limit = "1024"] +// Built-in Lints +#![deny(warnings, missing_copy_implementations)] +// Clippy lints +#![allow( + clippy::needless_doctest_main, + clippy::needless_pass_by_value, + clippy::option_map_unwrap_or_else, + clippy::option_map_unwrap_or +)] +#![warn( + clippy::wrong_pub_self_convention, + clippy::mut_mut, + clippy::non_ascii_literal, + clippy::similar_names, + clippy::unicode_not_nfc, + clippy::if_not_else, + clippy::items_after_statements, + clippy::used_underscore_binding +)] +#![cfg_attr(feature = "nightly", feature(proc_macro_diagnostic, proc_macro_span))] + +extern crate proc_macro; +extern crate proc_macro2; +#[macro_use] +extern crate quote; +#[macro_use] +extern crate syn; + +use proc_macro::TokenStream; + +mod diagnostic_shim; +mod field; +mod meta; +mod model; +mod resolved_at_shim; +mod util; + +mod as_changeset; +mod as_expression; +mod associations; +mod diesel_numeric_ops; +mod from_sql_row; +mod identifiable; +mod insertable; +mod query_id; +mod queryable; +mod queryable_by_name; +mod sql_function; +mod sql_type; +mod valid_grouping; + +use diagnostic_shim::*; + +/// Implements `AsChangeset` +/// +/// To implement `AsChangeset` this derive needs to know the corresponding table +/// type. By default it uses the `snake_case` type name with an added `s`. +/// It is possible to change this default by using `#[table_name = "something"]`. +/// In both cases the module for that table must be in scope. +/// For example, to derive this for a struct called `User`, you will +/// likely need a line such as `use schema::users;` +/// +/// If a field name of your struct differs +/// from the name of the corresponding column, you can annotate the field with +/// `#[column_name = "some_column_name"]`. +/// +/// By default, any `Option` fields on the struct are skipped if their value is +/// `None`. If you would like to assign `NULL` to the field instead, you can +/// annotate your struct with `#[changeset_options(treat_none_as_null = +/// "true")]`. +/// +/// # Attributes +/// +/// ## Optional type attributes +/// +/// * `#[table_name = "some_table"]`, specifies the table for which the +/// current type is a changeset. Requires that `some_table` is in scope. +/// If this attribute is not used, the type name converted to +/// `snake_case` with an added `s` is used as table name +/// * `#[changeset_options(treat_none_as_null = "true")]`, specifies that +/// the derive should threat `None` values as `NULL`. By default +/// `Option::::None` is just skipped. To insert a `NULL` using default +/// behavior use `Option::>::Some(None)` +/// +/// ## Optional field attributes +/// +/// * `#[column_name = "some_column_name"]`, overrides the column name +/// of the current field to `some_column_name`. By default the field +/// name is used as column name. +#[proc_macro_derive( + AsChangeset, + attributes(table_name, primary_key, column_name, changeset_options) +)] +pub fn derive_as_changeset(input: TokenStream) -> TokenStream { + expand_proc_macro(input, as_changeset::derive) +} + +/// Implements all required variants of `AsExpression` +/// +/// This derive will generate the following impls: +/// +/// - `impl AsExpression for YourType` +/// - `impl AsExpression> for YourType` +/// - `impl AsExpression for &'a YourType` +/// - `impl AsExpression> for &'a YourType` +/// - `impl AsExpression for &'a &'b YourType` +/// - `impl AsExpression> for &'a &'b YourType` +/// +/// If your type is unsized, +/// you can specify this by adding the annotation `#[diesel(not_sized)]` +/// as attribute on the type. This will skip the impls for non-reference types. +/// +/// # Attributes: +/// +/// ## Required type attributes +/// +/// * `#[sql_type = "SqlType"]`, to specify the sql type of the +/// generated implementations. If the attribute exists multiple times +/// impls for each sql type are generated. +/// +/// ## Optional type attribute +/// +/// * `#[diesel(not_sized)]`, to skip generating impls that require +/// that the type is `Sized` +#[proc_macro_derive(AsExpression, attributes(diesel, sql_type))] +pub fn derive_as_expression(input: TokenStream) -> TokenStream { + expand_proc_macro(input, as_expression::derive) +} + +/// Implement required traits for the associations API +/// +/// This derive implement support for diesels associations api. Check the +/// module level documentation of the `diesel::associations` module for details. +/// +/// # Attributes +/// +/// # Required type attributes +/// +/// * `#[belongs_to(User)]`, to specify a child-to-parent relation ship +/// between the current type and the specified parent type (`User`). +/// If this attribute is given multiple times, multiple relation ships +/// are generated. +/// * `#[belongs_to(User, foreign_key = "mykey")]`, variant of the attribute +/// above. Allows to specify the name of the foreign key. If the foreign key +/// is not specified explicitly, the remote lower case type name with an +/// appended `_id` is used as foreign key name. (`user_id` in this example +/// case) +/// +/// # Optional type attributes +/// +/// * `#[table_name = "some_table_name"]` specifies the table this +/// type belongs to. Requires that `some_table_name` is in scope. +/// If this attribute is not used, the type name converted to +/// `snake_case` with an added `s` is used as table name +/// +/// # Optional field attributes +/// +/// * `#[column_name = "some_table_name"]`, overrides the column the current +/// field maps to to `some_table_name`. By default the field name is used +/// as column name. Only useful for the foreign key field. +/// +#[proc_macro_derive(Associations, attributes(belongs_to, column_name, table_name))] +pub fn derive_associations(input: TokenStream) -> TokenStream { + expand_proc_macro(input, associations::derive) +} + +/// Implement numeric operators for the current query node +#[proc_macro_derive(DieselNumericOps)] +pub fn derive_diesel_numeric_ops(input: TokenStream) -> TokenStream { + expand_proc_macro(input, diesel_numeric_ops::derive) +} + +/// Implements `Queryable` for primitive types +/// +/// This derive is mostly useful to implement support deserializing +/// into rust types not supported by diesel itself. +/// +/// There are no options or special considerations needed for this derive. +#[proc_macro_derive(FromSqlRow, attributes(diesel))] +pub fn derive_from_sql_row(input: TokenStream) -> TokenStream { + expand_proc_macro(input, from_sql_row::derive) +} + +/// Implements `Identifiable` for references of the current type +/// +/// By default, the primary key field is assumed to be a single field called `id`. +/// If it's not, you can put `#[primary_key(your_id)]` on your struct. +/// If you have a composite primary key, the syntax is `#[primary_key(id1, id2)]`. +/// +/// By default, `#[derive(Identifiable)]` will assume that your table +/// name is the plural form of your struct name. +/// Diesel uses very simple pluralization rules. +/// It only adds an `s` to the end, and converts `CamelCase` to `snake_case`. +/// If your table name does not follow this convention +/// or the plural form isn't just an `s`, +/// you can specify the table name with `#[table_name = "some_table_name"]`. +/// In both cases the module for that table must be in scope. +/// For example, to derive this for a struct called `User`, you will +/// likely need a line such as `use schema::users;` +/// Our rules for inferring table names is considered public API. +/// It will never change without a major version bump. +/// +/// # Attributes +/// +/// ## Optional type attributes +/// +/// * `#[table_name = "some_table_name"]` specifies the table this +/// type belongs to. Requires that `some_table_name` is in scope. +/// If this attribute is not used, the type name converted to +/// `snake_case` with an added `s` is used as table name +/// * `#[primary_key(id1, id2)]` to specify the struct field that +/// that corresponds to the primary key. If not used, `id` will be +/// assumed as primary key field +/// +/// +#[proc_macro_derive(Identifiable, attributes(table_name, primary_key, column_name))] +pub fn derive_identifiable(input: TokenStream) -> TokenStream { + expand_proc_macro(input, identifiable::derive) +} + +/// Implements `Insertable` +/// +/// To implement `Insertable` this derive needs to know the corresponding table +/// type. By default it uses the `snake_case` type name with an added `s`. +/// It is possible to change this default by using `#[table_name = "something"]`. +/// In both cases the module for that table must be in scope. +/// For example, to derive this for a struct called `User`, you will +/// likely need a line such as `use schema::users;` +/// +/// If a field name of your +/// struct differs from the name of the corresponding column, +/// you can annotate the field with `#[column_name = "some_column_name"]`. +/// +/// Your struct can also contain fields which implement `Insertable`. This is +/// useful when you want to have one field map to more than one column (for +/// example, an enum that maps to a label and a value column). Add +/// `#[diesel(embed)]` to any such fields. +/// +/// To provide custom serialization behavior for a field, you can use +/// `#[diesel(serialize_as = "SomeType")]`. If this attribute is present, Diesel +/// will call `.into` on the corresponding field and serialize the instance of `SomeType`, +/// rather than the actual field on your struct. This can be used to add custom behavior for a +/// single field, or use types that are otherwise unsupported by Diesel. +/// Using `#[diesel(serialize_as)]` is **incompatible** with `#[diesel(embed)]`. +/// Normally, Diesel produces two implementations of the `Insertable` trait for your +/// struct using this derive: one for an owned version and one for a borrowed version. +/// Using `#[diesel(serialize_as)]` implies a conversion using `.into` which consumes the underlying value. +/// Hence, once you use `#[diesel(serialize_as)]`, Diesel can no longer insert borrowed +/// versions of your struct. +/// +/// # Attributes +/// +/// ## Optional type attributes +/// +/// * `#[table_name = "some_table_name"]`, specifies the table this type +/// is insertable into. Requires that `some_table_name` is in scope. +/// If this attribute is not used, the type name converted to +/// `snake_case` with an added `s` is used as table name +/// +/// ## Optional field attributes +/// +/// * `#[column_name = "some_table_name"]`, overrides the column the current +/// field maps to `some_table_name`. By default the field name is used +/// as column name +/// * `#[diesel(embed)]`, specifies that the current field maps not only +/// to single database field, but is a struct that implements `Insertable` +/// * `#[diesel(serialize_as = "SomeType")]`, instead of serializing the actual +/// field type, Diesel will convert the field into `SomeType` using `.into` and +/// serialize that instead. By default this derive will serialize directly using +/// the actual field type. +/// +/// # Examples +/// +/// If we want to customize the serialization during insert, we can use `#[diesel(serialize_as)]`. +/// +/// ```rust +/// # extern crate diesel; +/// # extern crate dotenv; +/// # include!("../../diesel/src/doctest_setup.rs"); +/// # use diesel::{prelude::*, serialize::{ToSql, Output, self}, deserialize::{FromSqlRow}, expression::AsExpression, sql_types, backend::Backend}; +/// # use schema::users; +/// # use std::io::Write; +/// # +/// #[derive(Debug, FromSqlRow, AsExpression)] +/// #[sql_type = "sql_types::Text"] +/// struct UppercaseString(pub String); +/// +/// impl Into for String { +/// fn into(self) -> UppercaseString { +/// UppercaseString(self.to_uppercase()) +/// } +/// } +/// +/// impl ToSql for UppercaseString +/// where +/// DB: Backend, +/// String: ToSql, +/// { +/// fn to_sql(&self, out: &mut Output) -> serialize::Result { +/// self.0.to_sql(out) +/// } +/// } +/// +/// #[derive(Insertable, PartialEq, Debug)] +/// #[table_name = "users"] +/// struct InsertableUser { +/// id: i32, +/// #[diesel(serialize_as = "UppercaseString")] +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # let connection = connection_no_data(); +/// # connection.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name VARCHAR(255) NOT NULL)").unwrap(); +/// let user = InsertableUser { +/// id: 1, +/// name: "thomas".to_string(), +/// }; +/// +/// diesel::insert_into(users) +/// .values(user) +/// .execute(&connection) +/// .unwrap(); +/// +/// assert_eq!( +/// Ok("THOMAS".to_string()), +/// users.select(name).first(&connection) +/// ); +/// # Ok(()) +/// # } +/// ``` + +#[proc_macro_derive(Insertable, attributes(table_name, column_name, diesel))] +pub fn derive_insertable(input: TokenStream) -> TokenStream { + expand_proc_macro(input, insertable::derive) +} + +#[doc(hidden)] +#[proc_macro_derive(NonAggregate)] +pub fn derive_non_aggregate(input: TokenStream) -> TokenStream { + eprintln!( + "#[derive(NonAggregate)] is deprecated. Please use \ + `#[derive(ValidGrouping)]` instead.)" + ); + expand_proc_macro(input, valid_grouping::derive) +} + +/// Implements `QueryId` +/// +/// For example, given this struct: +/// +/// ```rust +/// # extern crate diesel; +/// #[derive(diesel::query_builder::QueryId)] +/// pub struct And { +/// left: Left, +/// right: Right, +/// } +/// ``` +/// +/// the following implementation will be generated +/// +/// ```rust +/// # extern crate diesel; +/// # struct And(Left, Right); +/// # use diesel::query_builder::QueryId; +/// impl QueryId for And +/// where +/// Left: QueryId, +/// Right: QueryId, +/// { +/// type QueryId = And; +/// +/// const HAS_STATIC_QUERY_ID: bool = Left::HAS_STATIC_QUERY_ID && Right::HAS_STATIC_QUERY_ID; +/// } +/// ``` +/// +/// If the SQL generated by a struct is not uniquely identifiable by its type, +/// meaning that `HAS_STATIC_QUERY_ID` should always be false, +/// you should not derive this trait. +/// In that case you should implement it manually instead. +#[proc_macro_derive(QueryId)] +pub fn derive_query_id(input: TokenStream) -> TokenStream { + expand_proc_macro(input, query_id::derive) +} + +/// Implements `Queryable` to load the result of statically typed queries +/// +/// This trait can only be derived for structs, not enums. +/// +/// **When this trait is derived, it will assume that the order of fields on your +/// struct match the order of the fields in the query. This means that field +/// order is significant if you are using `#[derive(Queryable)]`. Field name has +/// no effect.** +/// +/// To provide custom deserialization behavior for a field, you can use +/// `#[diesel(deserialize_as = "SomeType")]`. If this attribute is present, Diesel +/// will deserialize the corresponding field into `SomeType`, rather than the +/// actual field type on your struct and then call `.into` to convert it to the +/// actual field type. This can be used to add custom behavior for a +/// single field, or use types that are otherwise unsupported by Diesel. +/// +/// # Attributes +/// +/// ## Optional field attributes: +/// +/// * `#[diesel(deserialize_as = "Type")]`, instead of deserializing directly +/// into the field type, the implementation will deserialize into `Type`. +/// Then `Type` is converted via `.into()` into the field type. By default +/// this derive will deserialize directly into the field type +/// +/// +/// # Examples +/// +/// If we just want to map a query to our struct, we can use `derive`. +/// +/// ```rust +/// # extern crate diesel; +/// # extern crate dotenv; +/// # include!("../../diesel/src/doctest_setup.rs"); +/// # +/// #[derive(Queryable, PartialEq, Debug)] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # let connection = establish_connection(); +/// let first_user = users.first(&connection)?; +/// let expected = User { id: 1, name: "Sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +/// +/// If we want to do additional work during deserialization, we can use +/// `deserialize_as` to use a different implementation. +/// +/// ```rust +/// # extern crate diesel; +/// # extern crate dotenv; +/// # include!("../../diesel/src/doctest_setup.rs"); +/// # +/// # use schema::users; +/// # use diesel::backend::{self, Backend}; +/// # use diesel::deserialize::{Queryable, FromSql}; +/// # use diesel::sql_types::Text; +/// # +/// struct LowercaseString(String); +/// +/// impl Into for LowercaseString { +/// fn into(self) -> String { +/// self.0 +/// } +/// } +/// +/// impl Queryable for LowercaseString +/// where +/// DB: Backend, +/// String: FromSql +/// { +/// +/// type Row = String; +/// +/// fn build(s: String) -> Self { +/// LowercaseString(s.to_lowercase()) +/// } +/// } +/// +/// #[derive(Queryable, PartialEq, Debug)] +/// struct User { +/// id: i32, +/// #[diesel(deserialize_as = "LowercaseString")] +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # let connection = establish_connection(); +/// let first_user = users.first(&connection)?; +/// let expected = User { id: 1, name: "sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +/// +/// Alternatively, we can implement the trait for our struct manually. +/// +/// ```rust +/// # extern crate diesel; +/// # extern crate dotenv; +/// # include!("../../diesel/src/doctest_setup.rs"); +/// # +/// use schema::users; +/// use diesel::deserialize::{Queryable, FromSqlRow}; +/// use diesel::row::Row; +/// +/// # /* +/// type DB = diesel::sqlite::Sqlite; +/// # */ +/// +/// #[derive(PartialEq, Debug)] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// impl Queryable for User +/// where +/// (i32, String): FromSqlRow, +/// { +/// type Row = (i32, String); +/// +/// fn build((id, name): Self::Row) -> Self { +/// User { id, name: name.to_lowercase() } +/// } +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # let connection = establish_connection(); +/// let first_user = users.first(&connection)?; +/// let expected = User { id: 1, name: "sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +#[proc_macro_derive(Queryable, attributes(column_name, diesel))] +pub fn derive_queryable(input: TokenStream) -> TokenStream { + expand_proc_macro(input, queryable::derive) +} + +/// Implements `QueryableByName` for untyped sql queries, such as that one generated +/// by `sql_query` +/// +/// To derive this trait, Diesel needs to know the SQL type of each field. You +/// can do this by either annotating your struct with `#[table_name = +/// "some_table"]` (in which case the SQL type will be +/// `diesel::dsl::SqlTypeOf`), or by annotating each +/// field with `#[sql_type = "SomeType"]`. +/// +/// If you are using `#[table_name]`, the module for that table must be in +/// scope. For example, to derive this for a struct called `User`, you will +/// likely need a line such as `use schema::users;` +/// +/// If the name of a field on your struct is different than the column in your +/// `table!` declaration, or if you are deriving this trait on a tuple struct, +/// you can annotate the field with `#[column_name = "some_column"]`. For tuple +/// structs, all fields must have this annotation. +/// +/// If a field is another struct which implements `QueryableByName`, +/// instead of a column, you can annotate that struct with `#[diesel(embed)]`. +/// Then all fields contained by that inner struct are loaded into +/// the embedded struct. +/// +/// To provide custom deserialization behavior for a field, you can use +/// `#[diesel(deserialize_as = "SomeType")]`. If this attribute is present, Diesel +/// will deserialize the corresponding field into `SomeType`, rather than the +/// actual field type on your struct and then call `.into` to convert it to the +/// actual field type. This can be used to add custom behavior for a +/// single field, or use types that are otherwise unsupported by Diesel. +/// +/// # Attributes +/// +/// ## Type attributes +/// +/// * `#[table_name = "some_table"]`, to specify that this type contains +/// columns for the specified table. If no field attributes are specified +/// the derive will use the sql type of the corresponding column. +/// +/// ## Field attributes +/// * `#[column_name = "some_column"]`, overrides the column name for +/// a given field. If not set, the name of the field is used as column +/// name. This attribute is required on tuple structs, if +/// `#[table_name = "some_table"]` is used, otherwise it's optional. +/// * `#[sql_type = "SomeType"]`, assumes `SomeType` as sql type of the +/// corresponding field. This attributes has precedence over all other +/// variants to specify the sql type. +/// * `#[diesel(deserialize_as = "Type")]`, instead of deserializing directly +/// into the field type, the implementation will deserialize into `Type`. +/// Then `Type` is converted via `.into()` into the field type. By default +/// this derive will deserialize directly into the field type +/// * `#[diesel(embed)]`, specifies that the current field maps not only +/// single database column, but is a type that implements +/// `QueryableByName` on it's own +/// +/// /// # Examples +/// +/// If we just want to map a query to our struct, we can use `derive`. +/// +/// ```rust +/// # extern crate diesel; +/// # extern crate dotenv; +/// # include!("../../diesel/src/doctest_setup.rs"); +/// # use schema::users; +/// # use diesel::sql_query; +/// # +/// #[derive(QueryableByName, PartialEq, Debug)] +/// #[table_name = "users"] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let connection = establish_connection(); +/// let first_user = sql_query("SELECT * FROM users ORDER BY id LIMIT 1") +/// .get_result(&connection)?; +/// let expected = User { id: 1, name: "Sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +/// +/// If we want to do additional work during deserialization, we can use +/// `deserialize_as` to use a different implementation. +/// +/// ```rust +/// # extern crate diesel; +/// # extern crate dotenv; +/// # include!("../../diesel/src/doctest_setup.rs"); +/// # use diesel::sql_query; +/// # use schema::users; +/// # use diesel::backend::{self, Backend}; +/// # use diesel::deserialize::{self, FromSql}; +/// # +/// struct LowercaseString(String); +/// +/// impl Into for LowercaseString { +/// fn into(self) -> String { +/// self.0 +/// } +/// } +/// +/// impl FromSql for LowercaseString +/// where +/// DB: Backend, +/// String: FromSql, +/// { +/// fn from_sql(bytes: backend::RawValue) -> deserialize::Result { +/// String::from_sql(bytes) +/// .map(|s| LowercaseString(s.to_lowercase())) +/// } +/// } +/// +/// #[derive(QueryableByName, PartialEq, Debug)] +/// #[table_name = "users"] +/// struct User { +/// id: i32, +/// #[diesel(deserialize_as = "LowercaseString")] +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let connection = establish_connection(); +/// let first_user = sql_query("SELECT * FROM users ORDER BY id LIMIT 1") +/// .get_result(&connection)?; +/// let expected = User { id: 1, name: "sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +/// +/// The custom derive generates impls similar to the follownig one +/// +/// ```rust +/// # extern crate diesel; +/// # extern crate dotenv; +/// # include!("../../diesel/src/doctest_setup.rs"); +/// # use schema::users; +/// # use diesel::sql_query; +/// # use diesel::deserialize::{self, QueryableByName, FromSql}; +/// # use diesel::row::NamedRow; +/// # use diesel::backend::Backend; +/// # +/// #[derive(PartialEq, Debug)] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// impl QueryableByName for User +/// where +/// DB: Backend, +/// i32: FromSql, DB>, +/// String: FromSql, DB>, +/// { +/// fn build<'a>(row: &impl NamedRow<'a, DB>) -> deserialize::Result { +/// let id = NamedRow::get::, _>(row, "id")?; +/// let name = NamedRow::get::, _>(row, "name")?; +/// +/// Ok(Self { id, name }) +/// } +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let connection = establish_connection(); +/// let first_user = sql_query("SELECT * FROM users ORDER BY id LIMIT 1") +/// .get_result(&connection)?; +/// let expected = User { id: 1, name: "Sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +#[proc_macro_derive(QueryableByName, attributes(table_name, column_name, sql_type, diesel))] +pub fn derive_queryable_by_name(input: TokenStream) -> TokenStream { + expand_proc_macro(input, queryable_by_name::derive) +} + +/// Implement necessary traits for adding a new sql type +/// +/// This trait implements all necessary traits to define a +/// new sql type. This is useful for adding support for unsupported +/// or custom types on sql side. The sql type will be usable for +/// all backends you specified via the attributes listed below. +/// +/// This derive will implement `NotNull`, `HasSqlType` and `SingleValue`. +/// When using this deriving, +/// you need to specify how the type is represented on various backends. +/// You don't need to specify every backend, +/// only the ones supported by your type. +/// +/// For PostgreSQL, add `#[postgres(type_name = "pg_type_name")]` +/// or `#[postgres(oid = "some_oid", array_oid = "some_oid")]` for +/// builtin types. +/// For MySQL, specify which variant of `MysqlType` should be used +/// by adding `#[mysql_type = "Variant"]`. +/// For SQLite, specify which variant of `SqliteType` should be used +/// by adding `#[sqlite_type = "Variant"]`. +/// +/// # Attributes +/// +/// ## Type attributes +/// +/// * `#[postgres(type_name = "TypeName")]` specifies support for +/// a postgresql type with the name `TypeName`. Prefer this variant +/// for types with no stable OID (== everything but the builtin types) +/// * `#[postgres(oid = 42, array_oid = 142)]`, specifies support for a +/// postgresql type with the given `oid` and `array_oid`. This variant +/// should only be used with types that have a stable OID. +/// * `#[sqlite_type = "TypeName"]`, specifies support for a sqlite type +/// with the given name. `TypeName` needs to be one of the possible values +/// in `SqliteType` +/// * `#[mysql_type = "TypeName"]`, specifies support for a mysql type +/// with the given name. `TypeName` needs to be one of the possible values +/// in `MysqlType` +#[proc_macro_derive(SqlType, attributes(postgres, sqlite_type, mysql_type))] +pub fn derive_sql_type(input: TokenStream) -> TokenStream { + expand_proc_macro(input, sql_type::derive) +} + +/// Implements `ValidGrouping` +/// +/// This trait can be automatically derived for structs with no type parameters +/// which are never aggregate, as well as for structs which are `NonAggregate` +/// when all type parameters are `NonAggregate`. For example: +/// +/// ```ignore +/// #[derive(ValidGrouping)] +/// struct LiteralOne; +/// +/// #[derive(ValidGrouping)] +/// struct Plus(Lhs, Rhs); +/// +/// // The following impl will be generated: +/// +/// impl ValidGrouping for LiteralOne { +/// type IsAggregate = is_aggregate::Never; +/// } +/// +/// impl ValidGrouping for Plus +/// where +/// Lhs: ValidGrouping, +/// Rhs: ValidGrouping, +/// Lhs::IsAggregate: MixedAggregates, +/// { +/// type IsAggregate = >::Output; +/// } +/// ``` +/// +/// For types which are always considered aggregate (such as an aggregate +/// function), annotate your struct with `#[diesel(aggregate)]` to set `IsAggregate` +/// explicitly to `is_aggregate::Yes`. +/// +/// # Attributes +/// +/// ## Optional type attributes +/// +/// * `#[diesel(aggregate)]` for cases where the type represents an aggregating +/// SQL expression +#[proc_macro_derive(ValidGrouping, attributes(diesel))] +pub fn derive_valid_grouping(input: TokenStream) -> TokenStream { + expand_proc_macro(input, valid_grouping::derive) +} + +/// Declare a sql function for use in your code. +/// +/// Diesel only provides support for a very small number of SQL functions. +/// This macro enables you to add additional functions from the SQL standard, +/// as well as any custom functions your application might have. +/// +/// The syntax for this macro is very similar to that of a normal Rust function, +/// except the argument and return types will be the SQL types being used. +/// Typically these types will come from [`diesel::sql_types`](../diesel/sql_types/index.html) +/// +/// This macro will generate two items. A function with the name that you've +/// given, and a module with a helper type representing the return type of your +/// function. For example, this invocation: +/// +/// ```ignore +/// sql_function!(fn lower(x: Text) -> Text); +/// ``` +/// +/// will generate this code: +/// +/// ```ignore +/// pub fn lower(x: X) -> lower::HelperType { +/// ... +/// } +/// +/// pub(crate) mod lower { +/// pub type HelperType = ...; +/// } +/// ``` +/// +/// If you are using this macro for part of a library, where the function is +/// part of your public API, it is highly recommended that you re-export this +/// helper type with the same name as your function. This is the standard +/// structure: +/// +/// ```ignore +/// pub mod functions { +/// use super::types::*; +/// use diesel::sql_types::*; +/// +/// sql_function! { +/// /// Represents the Pg `LENGTH` function used with `tsvector`s. +/// fn length(x: TsVector) -> Integer; +/// } +/// } +/// +/// pub mod helper_types { +/// /// The return type of `length(expr)` +/// pub type Length = functions::length::HelperType; +/// } +/// +/// pub mod dsl { +/// pub use functions::*; +/// pub use helper_types::*; +/// } +/// ``` +/// +/// Most attributes given to this macro will be put on the generated function +/// (including doc comments). +/// +/// # Adding Doc Comments +/// +/// ```no_run +/// # extern crate diesel; +/// # use diesel::*; +/// # +/// # table! { crates { id -> Integer, name -> VarChar, } } +/// # +/// use diesel::sql_types::Text; +/// +/// sql_function! { +/// /// Represents the `canon_crate_name` SQL function, created in +/// /// migration .... +/// fn canon_crate_name(a: Text) -> Text; +/// } +/// +/// # fn main() { +/// # use self::crates::dsl::*; +/// let target_name = "diesel"; +/// crates.filter(canon_crate_name(name).eq(canon_crate_name(target_name))); +/// // This will generate the following SQL +/// // SELECT * FROM crates WHERE canon_crate_name(crates.name) = canon_crate_name($1) +/// # } +/// ``` +/// +/// # Special Attributes +/// +/// There are a handful of special attributes that Diesel will recognize. They +/// are: +/// +/// - `#[aggregate]` +/// - Indicates that this is an aggregate function, and that `NonAggregate` +/// should not be implemented. +/// - `#[sql_name="name"]` +/// - The SQL to be generated is different than the Rust name of the function. +/// This can be used to represent functions which can take many argument +/// types, or to capitalize function names. +/// +/// Functions can also be generic. Take the definition of `sum` for an example +/// of all of this: +/// +/// ```no_run +/// # extern crate diesel; +/// # use diesel::*; +/// # +/// # table! { crates { id -> Integer, name -> VarChar, } } +/// # +/// use diesel::sql_types::{Foldable, Nullable}; +/// +/// sql_function! { +/// #[aggregate] +/// #[sql_name = "SUM"] +/// fn sum(expr: Nullable) -> ST::Sum; +/// } +/// +/// # fn main() { +/// # use self::crates::dsl::*; +/// crates.select(sum(id)); +/// # } +/// ``` +/// +/// # SQL Functions without Arguments +/// +/// A common example is ordering a query using the `RANDOM()` sql function, +/// which can be implemented using `sql_function!` like this: +/// +/// ```rust +/// # extern crate diesel; +/// # use diesel::*; +/// # +/// # table! { crates { id -> Integer, name -> VarChar, } } +/// # +/// sql_function!(fn random() -> Text); +/// +/// # fn main() { +/// # use self::crates::dsl::*; +/// crates.order(random()); +/// # } +/// ``` +/// +/// # Use with SQLite +/// +/// On most backends, the implementation of the function is defined in a +/// migration using `CREATE FUNCTION`. On SQLite, the function is implemented in +/// Rust instead. You must call `register_impl` or +/// `register_nondeterministic_impl` with every connection before you can use +/// the function. +/// +/// These functions will only be generated if the `sqlite` feature is enabled, +/// and the function is not generic. Generic functions and variadic functions +/// are not supported on SQLite. +/// +/// ```rust +/// # extern crate diesel; +/// # use diesel::*; +/// # +/// # #[cfg(feature = "sqlite")] +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// # +/// # #[cfg(not(feature = "sqlite"))] +/// # fn main() { +/// # } +/// # +/// use diesel::sql_types::{Integer, Double}; +/// sql_function!(fn add_mul(x: Integer, y: Integer, z: Double) -> Double); +/// +/// # #[cfg(feature = "sqlite")] +/// # fn run_test() -> Result<(), Box<::std::error::Error>> { +/// let connection = SqliteConnection::establish(":memory:")?; +/// +/// add_mul::register_impl(&connection, |x: i32, y: i32, z: f64| { +/// (x + y) as f64 * z +/// })?; +/// +/// let result = select(add_mul(1, 2, 1.5)) +/// .get_result::(&connection)?; +/// assert_eq!(4.5, result); +/// # Ok(()) +/// # } +/// ``` +/// +/// ## Custom Aggregate Functions +/// +/// Custom aggregate functions can be created in SQLite by adding an `#[aggregate]` +/// attribute inside of `sql_function`. `register_impl` needs to be called on +/// the generated function with a type implementing the +/// [SqliteAggregateFunction](../diesel/sqlite/trait.SqliteAggregateFunction.html) +/// trait as a type parameter as shown in the examples below. +/// +/// ```rust +/// # extern crate diesel; +/// # use diesel::*; +/// # +/// # #[cfg(feature = "sqlite")] +/// # fn main() { +/// # run().unwrap(); +/// # } +/// # +/// # #[cfg(not(feature = "sqlite"))] +/// # fn main() { +/// # } +/// use diesel::sql_types::Integer; +/// # #[cfg(feature = "sqlite")] +/// use diesel::sqlite::SqliteAggregateFunction; +/// +/// sql_function! { +/// #[aggregate] +/// fn my_sum(x: Integer) -> Integer; +/// } +/// +/// #[derive(Default)] +/// struct MySum { sum: i32 } +/// +/// # #[cfg(feature = "sqlite")] +/// impl SqliteAggregateFunction for MySum { +/// type Output = i32; +/// +/// fn step(&mut self, expr: i32) { +/// self.sum += expr; +/// } +/// +/// fn finalize(aggregator: Option) -> Self::Output { +/// aggregator.map(|a| a.sum).unwrap_or_default() +/// } +/// } +/// # table! { +/// # players { +/// # id -> Integer, +/// # score -> Integer, +/// # } +/// # } +/// +/// # #[cfg(feature = "sqlite")] +/// fn run() -> Result<(), Box> { +/// # use self::players::dsl::*; +/// let connection = SqliteConnection::establish(":memory:")?; +/// # connection.execute("create table players (id integer primary key autoincrement, score integer)").unwrap(); +/// # connection.execute("insert into players (score) values (10), (20), (30)").unwrap(); +/// +/// my_sum::register_impl::(&connection)?; +/// +/// let total_score = players.select(my_sum(score)) +/// .get_result::(&connection)?; +/// +/// println!("The total score of all the players is: {}", total_score); +/// +/// # assert_eq!(60, total_score); +/// Ok(()) +/// } +/// ``` +/// +/// With multiple function arguments the arguments are passed as a tuple to `SqliteAggregateFunction` +/// +/// ```rust +/// # extern crate diesel; +/// # use diesel::*; +/// # +/// # #[cfg(feature = "sqlite")] +/// # fn main() { +/// # run().unwrap(); +/// # } +/// # +/// # #[cfg(not(feature = "sqlite"))] +/// # fn main() { +/// # } +/// use diesel::sql_types::{Float, Nullable}; +/// # #[cfg(feature = "sqlite")] +/// use diesel::sqlite::SqliteAggregateFunction; +/// +/// sql_function! { +/// #[aggregate] +/// fn range_max(x0: Float, x1: Float) -> Nullable; +/// } +/// +/// #[derive(Default)] +/// struct RangeMax { max_value: Option } +/// +/// # #[cfg(feature = "sqlite")] +/// impl SqliteAggregateFunction<(T, T)> for RangeMax { +/// type Output = Option; +/// +/// fn step(&mut self, (x0, x1): (T, T)) { +/// # let max = if x0 >= x1 { +/// # x0 +/// # } else { +/// # x1 +/// # }; +/// # +/// # self.max_value = match self.max_value { +/// # Some(current_max_value) if max > current_max_value => Some(max), +/// # None => Some(max), +/// # _ => self.max_value, +/// # }; +/// // Compare self.max_value to x0 and x1 +/// } +/// +/// fn finalize(aggregator: Option) -> Self::Output { +/// aggregator?.max_value +/// } +/// } +/// # table! { +/// # student_avgs { +/// # id -> Integer, +/// # s1_avg -> Float, +/// # s2_avg -> Float, +/// # } +/// # } +/// +/// # #[cfg(feature = "sqlite")] +/// fn run() -> Result<(), Box> { +/// # use self::student_avgs::dsl::*; +/// let connection = SqliteConnection::establish(":memory:")?; +/// # connection.execute("create table student_avgs (id integer primary key autoincrement, s1_avg float, s2_avg float)").unwrap(); +/// # connection.execute("insert into student_avgs (s1_avg, s2_avg) values (85.5, 90), (79.8, 80.1)").unwrap(); +/// +/// range_max::register_impl::, _, _>(&connection)?; +/// +/// let result = student_avgs.select(range_max(s1_avg, s2_avg)) +/// .get_result::>(&connection)?; +/// +/// if let Some(max_semeseter_avg) = result { +/// println!("The largest semester average is: {}", max_semeseter_avg); +/// } +/// +/// # assert_eq!(Some(90f32), result); +/// Ok(()) +/// } +/// ``` +#[proc_macro] +pub fn sql_function_proc(input: TokenStream) -> TokenStream { + expand_proc_macro(input, sql_function::expand) +} + +fn expand_proc_macro( + input: TokenStream, + f: fn(T) -> Result, +) -> TokenStream { + let item = syn::parse(input).unwrap(); + match f(item) { + Ok(x) => x.into(), + Err(e) => { + e.emit(); + "".parse().unwrap() + } + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/meta.rs b/collector/benchmarks/diesel/diesel_derives/src/meta.rs new file mode 100644 index 000000000..9d045de26 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/meta.rs @@ -0,0 +1,272 @@ +use proc_macro2::{Ident, Span}; +use syn; +use syn::fold::Fold; +use syn::spanned::Spanned; + +use resolved_at_shim::*; +use util::*; + +pub struct MetaItem { + meta: syn::Meta, +} + +pub(crate) fn path_to_string(path: &syn::Path) -> String { + path.segments + .iter() + .map(|s| s.ident.to_string()) + .collect::>() + .join("::") +} + +impl MetaItem { + pub fn all_with_name(attrs: &[syn::Attribute], name: &str) -> Vec { + attrs + .iter() + .filter_map(|attr| { + attr.parse_meta() + .ok() + .map(|m| FixSpan(attr.pound_token.spans[0]).fold_meta(m)) + }) + .filter(|m| m.path().is_ident(name)) + .map(|meta| Self { meta }) + .collect() + } + + pub fn with_name(attrs: &[syn::Attribute], name: &str) -> Option { + Self::all_with_name(attrs, name).pop() + } + + pub fn empty(name: &str) -> Self { + Self { + meta: syn::Meta::List(syn::MetaList { + path: syn::Path::from(Ident::new(name, Span::call_site())), + paren_token: Default::default(), + nested: Default::default(), + }), + } + } + + pub fn nested_item(&self, name: &str) -> Result, Diagnostic> { + self.nested() + .map(|mut i| i.find(|n| n.name().is_ident(name))) + } + + pub fn required_nested_item(&self, name: &str) -> Result { + self.nested_item(name)?.ok_or_else(|| { + self.span() + .error(format!("Missing required option `{}`", name)) + }) + } + + pub fn expect_bool_value(&self) -> bool { + match self.str_value().as_ref().map(String::as_str) { + Ok("true") => true, + Ok("false") => false, + _ => { + self.span() + .error(format!( + "`{0}` must be in the form `{0} = \"true\"`", + path_to_string(&self.name()) + )) + .emit(); + false + } + } + } + + pub fn expect_ident_value(&self) -> syn::Ident { + self.ident_value().unwrap_or_else(|e| { + e.emit(); + self.name().segments.first().unwrap().ident.clone() + }) + } + + pub fn ident_value(&self) -> Result { + let maybe_attr = self.nested().ok().and_then(|mut n| n.next()); + let maybe_path = maybe_attr.as_ref().and_then(|m| m.path().ok()); + match maybe_path { + Some(x) => { + self.span() + .warning(format!( + "The form `{0}(value)` is deprecated. Use `{0} = \"value\"` instead", + path_to_string(&self.name()), + )) + .emit(); + Ok(x.segments.first().unwrap().ident.clone()) + } + _ => Ok(syn::Ident::new( + &self.str_value()?, + self.value_span().resolved_at(Span::call_site()), + )), + } + } + + pub fn expect_path(&self) -> syn::Path { + self.path().unwrap_or_else(|e| { + e.emit(); + self.name() + }) + } + + pub fn path(&self) -> Result { + use syn::Meta::*; + + match self.meta { + Path(ref x) => Ok(x.clone()), + _ => { + let meta = &self.meta; + Err(self.span().error(format!( + "Expected `{}` found `{}`", + path_to_string(&self.name()), + quote!(#meta) + ))) + } + } + } + + pub fn nested(&self) -> Result { + use syn::Meta::*; + + match self.meta { + List(ref list) => Ok(Nested(list.nested.iter())), + _ => Err(self.span().error(format!( + "`{0}` must be in the form `{0}(...)`", + path_to_string(&self.name()) + ))), + } + } + + pub fn name(&self) -> syn::Path { + self.meta.path().clone() + } + + pub fn has_flag(&self, flag: &str) -> bool { + self.nested() + .map(|mut n| { + n.any(|m| match m.path() { + Ok(word) => word.is_ident(flag), + Err(_) => false, + }) + }) + .unwrap_or_else(|e| { + e.emit(); + false + }) + } + + pub fn ty_value(&self) -> Result { + let str = self.lit_str_value()?; + str.parse() + .map_err(|_| str.span().error("Invalid Rust type")) + } + + pub fn expect_str_value(&self) -> String { + self.str_value().unwrap_or_else(|e| { + e.emit(); + path_to_string(&self.name()) + }) + } + + pub fn str_value(&self) -> Result { + self.lit_str_value().map(syn::LitStr::value) + } + + fn lit_str_value(&self) -> Result<&syn::LitStr, Diagnostic> { + use syn::Lit::*; + + match *self.lit_value()? { + Str(ref s) => Ok(s), + _ => Err(self.span().error(format!( + "`{0}` must be in the form `{0} = \"value\"`", + path_to_string(&self.name()) + ))), + } + } + + pub fn expect_int_value(&self) -> u64 { + self.int_value().emit_error().unwrap_or(0) + } + + pub fn int_value(&self) -> Result { + use syn::Lit::*; + + let error = self.value_span().error("Expected a number"); + + match *self.lit_value()? { + Str(ref s) => s.value().parse().map_err(|_| error), + Int(ref i) => i.base10_parse().map_err(|_| error), + _ => Err(error), + } + } + + fn lit_value(&self) -> Result<&syn::Lit, Diagnostic> { + use syn::Meta::*; + + match self.meta { + NameValue(ref name_value) => Ok(&name_value.lit), + _ => Err(self.span().error(format!( + "`{0}` must be in the form `{0} = \"value\"`", + path_to_string(&self.name()) + ))), + } + } + + pub fn warn_if_other_options(&self, options: &[&str]) { + let nested = match self.nested() { + Ok(x) => x, + Err(_) => return, + }; + let unrecognized_options = + nested.filter(|n| !options.iter().any(|&o| n.name().is_ident(o))); + for ignored in unrecognized_options { + ignored + .span() + .warning(format!( + "Option {} has no effect", + path_to_string(&ignored.name()) + )) + .emit(); + } + } + + fn value_span(&self) -> Span { + use syn::Meta::*; + + match self.meta { + Path(ref path) => path.span(), + List(ref meta) => meta.nested.span(), + NameValue(ref meta) => meta.lit.span(), + } + } + + pub fn span(&self) -> Span { + self.meta.span() + } +} + +pub struct Nested<'a>(syn::punctuated::Iter<'a, syn::NestedMeta>); + +impl<'a> Iterator for Nested<'a> { + type Item = MetaItem; + + fn next(&mut self) -> Option { + use syn::NestedMeta::*; + + match self.0.next() { + Some(&Meta(ref item)) => Some(MetaItem { meta: item.clone() }), + Some(_) => self.next(), + None => None, + } + } +} + +/// If the given span is affected by +/// , +/// returns the span of the pound token +struct FixSpan(Span); + +impl Fold for FixSpan { + fn fold_span(&mut self, span: Span) -> Span { + fix_span(span, self.0) + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/model.rs b/collector/benchmarks/diesel/diesel_derives/src/model.rs new file mode 100644 index 000000000..b3c526d19 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/model.rs @@ -0,0 +1,112 @@ +use proc_macro2::{Ident, Span}; +use syn; + +use diagnostic_shim::*; +use field::*; +use meta::*; +use resolved_at_shim::*; + +pub struct Model { + pub name: syn::Ident, + pub primary_key_names: Vec, + table_name_from_attribute: Option, + fields: Vec, +} + +impl Model { + pub fn from_item(item: &syn::DeriveInput) -> Result { + let table_name_from_attribute = + MetaItem::with_name(&item.attrs, "table_name").map(|m| m.expect_ident_value()); + let primary_key_names = MetaItem::with_name(&item.attrs, "primary_key") + .map(|m| { + Ok(m.nested()? + .map(|m| m.expect_path().segments.first().unwrap().ident.clone()) + .collect()) + }) + .unwrap_or_else(|| Ok(vec![Ident::new("id", Span::call_site())]))?; + let fields = fields_from_item_data(&item.data)?; + Ok(Self { + name: item.ident.clone(), + table_name_from_attribute, + primary_key_names, + fields, + }) + } + + pub fn table_name(&self) -> syn::Ident { + self.table_name_from_attribute.clone().unwrap_or_else(|| { + syn::Ident::new( + &infer_table_name(&self.name.to_string()), + self.name.span().resolved_at(Span::call_site()), + ) + }) + } + + pub fn fields(&self) -> &[Field] { + &self.fields + } + + pub fn find_column(&self, column_name: &syn::Ident) -> Result<&Field, Diagnostic> { + self.fields() + .iter() + .find(|f| &f.column_name() == column_name) + .ok_or_else(|| { + column_name + .span() + .error(format!("No field with column name {}", column_name)) + }) + } + + pub fn has_table_name_attribute(&self) -> bool { + self.table_name_from_attribute.is_some() + } +} + +pub fn camel_to_snake(name: &str) -> String { + let mut result = String::with_capacity(name.len()); + result.push_str(&name[..1].to_lowercase()); + for character in name[1..].chars() { + if character.is_uppercase() { + result.push('_'); + for lowercase in character.to_lowercase() { + result.push(lowercase); + } + } else { + result.push(character); + } + } + result +} + +fn infer_table_name(name: &str) -> String { + let mut result = camel_to_snake(name); + result.push('s'); + result +} + +fn fields_from_item_data(data: &syn::Data) -> Result, Diagnostic> { + use syn::Data::*; + + let struct_data = match *data { + Struct(ref d) => d, + _ => return Err(Span::call_site().error("This derive can only be used on structs")), + }; + Ok(struct_data + .fields + .iter() + .enumerate() + .map(|(i, f)| Field::from_struct_field(f, i)) + .collect()) +} + +#[test] +fn infer_table_name_pluralizes_and_downcases() { + assert_eq!("foos", &infer_table_name("Foo")); + assert_eq!("bars", &infer_table_name("Bar")); +} + +#[test] +fn infer_table_name_properly_handles_underscores() { + assert_eq!("foo_bars", &infer_table_name("FooBar")); + assert_eq!("foo_bar_bazs", &infer_table_name("FooBarBaz")); +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/query_id.rs b/collector/benchmarks/diesel/diesel_derives/src/query_id.rs new file mode 100644 index 000000000..bfa561ca0 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/query_id.rs @@ -0,0 +1,37 @@ +use proc_macro2; +use syn; + +use util::*; + +pub fn derive(mut item: syn::DeriveInput) -> Result { + for ty_param in item.generics.type_params_mut() { + ty_param.bounds.push(parse_quote!(QueryId)); + } + let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); + + let struct_name = &item.ident; + let lifetimes = item.generics.lifetimes(); + let query_id_ty_params = item + .generics + .type_params() + .map(|ty_param| &ty_param.ident) + .map(|ty_param| quote!(<#ty_param as QueryId>::QueryId)); + let has_static_query_id = item + .generics + .type_params() + .map(|ty_param| &ty_param.ident) + .map(|ty_param| quote!(<#ty_param as QueryId>::HAS_STATIC_QUERY_ID)); + + Ok(wrap_in_dummy_mod(quote! { + use diesel::query_builder::QueryId; + + #[allow(non_camel_case_types)] + impl #impl_generics QueryId for #struct_name #ty_generics + #where_clause + { + type QueryId = #struct_name<#(#lifetimes,)* #(#query_id_ty_params,)*>; + + const HAS_STATIC_QUERY_ID: bool = #(#has_static_query_id &&)* true; + } + })) +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/queryable.rs b/collector/benchmarks/diesel/diesel_derives/src/queryable.rs new file mode 100644 index 000000000..388a2f880 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/queryable.rs @@ -0,0 +1,63 @@ +use proc_macro2; +use syn; + +use field::Field; +use model::*; +use util::*; + +pub fn derive(item: syn::DeriveInput) -> Result { + let model = Model::from_item(&item)?; + + let struct_name = &item.ident; + let field_ty = model + .fields() + .iter() + .map(Field::ty_for_deserialize) + .collect::, _>>()?; + let field_ty = &field_ty; + let build_expr = model.fields().iter().enumerate().map(|(i, f)| { + let i = syn::Index::from(i); + f.name.assign(parse_quote!(row.#i.into())) + }); + let sql_type = (0..model.fields().len()) + .map(|i| { + let i = syn::Ident::new(&format!("__ST{}", i), proc_macro2::Span::call_site()); + quote!(#i) + }) + .collect::>(); + let sql_type = &sql_type; + + let (_, ty_generics, _) = item.generics.split_for_impl(); + let mut generics = item.generics.clone(); + generics + .params + .push(parse_quote!(__DB: diesel::backend::Backend)); + for id in 0..model.fields().len() { + let ident = syn::Ident::new(&format!("__ST{}", id), proc_macro2::Span::call_site()); + generics.params.push(parse_quote!(#ident)); + } + { + let where_clause = generics.where_clause.get_or_insert(parse_quote!(where)); + where_clause + .predicates + .push(parse_quote!((#(#field_ty,)*): FromStaticSqlRow<(#(#sql_type,)*), __DB>)); + } + let (impl_generics, _, where_clause) = generics.split_for_impl(); + + Ok(wrap_in_dummy_mod(quote! { + use diesel::deserialize::{FromStaticSqlRow, Queryable}; + use diesel::row::{Row, Field}; + + impl #impl_generics Queryable<(#(#sql_type,)*), __DB> for #struct_name #ty_generics + #where_clause + { + type Row = (#(#field_ty,)*); + + fn build(row: Self::Row) -> Self { + Self { + #(#build_expr,)* + } + } + } + })) +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/queryable_by_name.rs b/collector/benchmarks/diesel/diesel_derives/src/queryable_by_name.rs new file mode 100644 index 000000000..bc954d5b7 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/queryable_by_name.rs @@ -0,0 +1,120 @@ +use proc_macro2::{self, Ident, Span}; +use syn; + +use field::*; +use model::*; +use util::*; + +pub fn derive(item: syn::DeriveInput) -> Result { + let model = Model::from_item(&item)?; + + let struct_name = &item.ident; + let fields = model.fields().iter().map(get_ident).collect::>(); + let field_names = model.fields().iter().map(|f| &f.name).collect::>(); + + let initial_field_expr = model + .fields() + .iter() + .map(|f| { + if f.has_flag("embed") { + let field_ty = &f.ty; + Ok(quote!(<#field_ty as QueryableByName<__DB>>::build( + row, + )?)) + } else { + let name = f.column_name(); + let field_ty = &f.ty; + let deserialize_ty = f.ty_for_deserialize()?; + Ok(quote!( + { + let field = diesel::row::NamedRow::get(row, stringify!(#name))?; + <#deserialize_ty as Into<#field_ty>>::into(field) + } + )) + } + }) + .collect::, Diagnostic>>()?; + + let (_, ty_generics, ..) = item.generics.split_for_impl(); + let mut generics = item.generics.clone(); + generics + .params + .push(parse_quote!(__DB: diesel::backend::Backend)); + + for field in model.fields() { + let where_clause = generics.where_clause.get_or_insert(parse_quote!(where)); + let field_ty = field.ty_for_deserialize()?; + if field.has_flag("embed") { + where_clause + .predicates + .push(parse_quote!(#field_ty: QueryableByName<__DB>)); + } else { + let st = sql_type(field, &model); + where_clause + .predicates + .push(parse_quote!(#field_ty: diesel::deserialize::FromSql<#st, __DB>)); + } + } + + let (impl_generics, _, where_clause) = generics.split_for_impl(); + + Ok(wrap_in_dummy_mod(quote! { + use diesel::deserialize::{self, QueryableByName}; + use diesel::row::{NamedRow}; + use diesel::sql_types::Untyped; + + impl #impl_generics QueryableByName<__DB> + for #struct_name #ty_generics + #where_clause + { + fn build<'__a>(row: &impl NamedRow<'__a, __DB>) -> deserialize::Result + { + + + #( + let mut #fields = #initial_field_expr; + )* + deserialize::Result::Ok(Self { + #( + #field_names: #fields, + )* + }) + } + } + })) +} + +fn get_ident(field: &Field) -> Ident { + match &field.name { + FieldName::Named(n) => n.clone(), + FieldName::Unnamed(i) => Ident::new(&format!("field_{}", i.index), Span::call_site()), + } +} + +fn sql_type(field: &Field, model: &Model) -> syn::Type { + let table_name = model.table_name(); + let column_name = field.column_name(); + + match field.sql_type { + Some(ref st) => st.clone(), + None => { + if model.has_table_name_attribute() { + parse_quote!(diesel::dsl::SqlTypeOf<#table_name::#column_name>) + } else { + let field_name = match field.name { + FieldName::Named(ref x) => x.clone(), + _ => Ident::new("field", Span::call_site()), + }; + field + .span + .error(format!("Cannot determine the SQL type of {}", field_name)) + .help( + "Your struct must either be annotated with `#[table_name = \"foo\"]` \ + or have all of its fields annotated with `#[sql_type = \"Integer\"]`", + ) + .emit(); + parse_quote!(()) + } + } + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/resolved_at_shim.rs b/collector/benchmarks/diesel/diesel_derives/src/resolved_at_shim.rs new file mode 100644 index 000000000..280873344 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/resolved_at_shim.rs @@ -0,0 +1,19 @@ +use proc_macro2::Span; + +pub trait ResolvedAtExt { + fn resolved_at(self, span: Span) -> Span; +} + +#[cfg(feature = "nightly")] +impl ResolvedAtExt for Span { + fn resolved_at(self, span: Span) -> Span { + self.unstable().resolved_at(span.unstable()).into() + } +} + +#[cfg(not(feature = "nightly"))] +impl ResolvedAtExt for Span { + fn resolved_at(self, _: Span) -> Span { + self + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/sql_function.rs b/collector/benchmarks/diesel/diesel_derives/src/sql_function.rs new file mode 100644 index 000000000..92813b570 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/sql_function.rs @@ -0,0 +1,384 @@ +use proc_macro2::*; +use quote::ToTokens; +use syn::parse::{self, Parse, ParseStream}; +use syn::punctuated::Punctuated; + +use meta::*; +use util::*; + +// Extremely curious why this triggers on a nearly branchless function +#[allow(clippy::cognitive_complexity)] +// for loop comes from `quote!` +#[allow(clippy::for_loop_over_option)] +// https://github.com/rust-lang/rust-clippy/issues/3768 +#[allow(clippy::useless_let_if_seq)] +pub(crate) fn expand(input: SqlFunctionDecl) -> Result { + let SqlFunctionDecl { + mut attributes, + fn_token, + fn_name, + mut generics, + args, + return_type, + } = input; + + let sql_name = MetaItem::with_name(&attributes, "sql_name") + .map(|m| m.str_value()) + .unwrap_or_else(|| Ok(fn_name.to_string()))?; + let is_aggregate = MetaItem::with_name(&attributes, "aggregate").is_some(); + + attributes.retain(|attr| { + attr.parse_meta() + .map(|m| !m.path().is_ident("sql_name") && !m.path().is_ident("aggregate")) + .unwrap_or(true) + }); + + let args = &args; + let (ref arg_name, ref arg_type): (Vec<_>, Vec<_>) = args + .iter() + .map(|StrictFnArg { name, ty, .. }| (name, ty)) + .unzip(); + let arg_struct_assign = args.iter().map( + |StrictFnArg { + name, colon_token, .. + }| { + let name2 = name.clone(); + quote!(#name #colon_token #name2.as_expression()) + }, + ); + + let type_args = &generics + .type_params() + .map(|type_param| type_param.ident.clone()) + .collect::>(); + + for StrictFnArg { name, .. } in args { + generics.params.push(parse_quote!(#name)); + } + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + // Even if we force an empty where clause, it still won't print the where + // token with no bounds. + let where_clause = where_clause + .map(|w| quote!(#w)) + .unwrap_or_else(|| quote!(where)); + + let mut generics_with_internal = generics.clone(); + generics_with_internal + .params + .push(parse_quote!(__DieselInternal)); + let (impl_generics_internal, _, _) = generics_with_internal.split_for_impl(); + + let sql_type; + let numeric_derive; + + if arg_name.is_empty() { + sql_type = None; + // FIXME: We can always derive once trivial bounds are stable + numeric_derive = None; + } else { + sql_type = Some(quote!((#(#arg_name),*): Expression,)); + numeric_derive = Some(quote!(#[derive(diesel::sql_types::DieselNumericOps)])); + } + + let args_iter = args.iter(); + let mut tokens = quote! { + use diesel::{self, QueryResult}; + use diesel::expression::{AsExpression, Expression, SelectableExpression, AppearsOnTable, ValidGrouping}; + use diesel::query_builder::{QueryFragment, AstPass}; + use diesel::sql_types::*; + use super::*; + + #[derive(Debug, Clone, Copy, diesel::query_builder::QueryId)] + #numeric_derive + pub struct #fn_name #ty_generics { + #(pub(in super) #args_iter,)* + #(pub(in super) #type_args: ::std::marker::PhantomData<#type_args>,)* + } + + pub type HelperType #ty_generics = #fn_name < + #(#type_args,)* + #(<#arg_name as AsExpression<#arg_type>>::Expression,)* + >; + + impl #impl_generics Expression for #fn_name #ty_generics + #where_clause + #sql_type + { + type SqlType = #return_type; + } + + // __DieselInternal is what we call QS normally + impl #impl_generics_internal SelectableExpression<__DieselInternal> + for #fn_name #ty_generics + #where_clause + #(#arg_name: SelectableExpression<__DieselInternal>,)* + Self: AppearsOnTable<__DieselInternal>, + { + } + + // __DieselInternal is what we call QS normally + impl #impl_generics_internal AppearsOnTable<__DieselInternal> + for #fn_name #ty_generics + #where_clause + #(#arg_name: AppearsOnTable<__DieselInternal>,)* + Self: Expression, + { + } + + // __DieselInternal is what we call DB normally + impl #impl_generics_internal QueryFragment<__DieselInternal> + for #fn_name #ty_generics + where + __DieselInternal: diesel::backend::Backend, + for<'a> (#(&'a #arg_name),*): QueryFragment<__DieselInternal>, + { + fn walk_ast(&self, mut out: AstPass<__DieselInternal>) -> QueryResult<()> { + out.push_sql(concat!(#sql_name, "(")); + (#(&self.#arg_name,)*).walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } + } + }; + + if is_aggregate { + tokens = quote! { + #tokens + + impl #impl_generics_internal ValidGrouping<__DieselInternal> + for #fn_name #ty_generics + { + type IsAggregate = diesel::expression::is_aggregate::Yes; + } + }; + if cfg!(feature = "sqlite") && type_args.is_empty() { + tokens = quote! { + #tokens + + use diesel::sqlite::{Sqlite, SqliteConnection}; + use diesel::serialize::ToSql; + use diesel::deserialize::{FromSqlRow, StaticallySizedRow}; + use diesel::sqlite::SqliteAggregateFunction; + use diesel::sql_types::IntoNullable; + }; + + match arg_name.len() { + x if x > 1 => { + tokens = quote! { + #tokens + + #[allow(dead_code)] + /// Registers an implementation for this aggregate function on the given connection + /// + /// This function must be called for every `SqliteConnection` before + /// this SQL function can be used on SQLite. The implementation must be + /// deterministic (returns the same result given the same arguments). + pub fn register_impl( + conn: &SqliteConnection + ) -> QueryResult<()> + where + A: SqliteAggregateFunction<(#(#arg_name,)*)> + Send + 'static, + A::Output: ToSql<#return_type, Sqlite>, + (#(#arg_name,)*): FromSqlRow<(#(#arg_type,)*), Sqlite> + + StaticallySizedRow<(#(#arg_type,)*), Sqlite>, + { + conn.register_aggregate_function::<(#(#arg_type,)*), #return_type, _, _, A>(#sql_name) + } + }; + } + x if x == 1 => { + let arg_name = arg_name[0]; + let arg_type = arg_type[0]; + + tokens = quote! { + #tokens + + #[allow(dead_code)] + /// Registers an implementation for this aggregate function on the given connection + /// + /// This function must be called for every `SqliteConnection` before + /// this SQL function can be used on SQLite. The implementation must be + /// deterministic (returns the same result given the same arguments). + pub fn register_impl( + conn: &SqliteConnection + ) -> QueryResult<()> + where + A: SqliteAggregateFunction<#arg_name> + Send + 'static, + A::Output: ToSql<#return_type, Sqlite>, + #arg_name: FromSqlRow<#arg_type, Sqlite> + + StaticallySizedRow<#arg_type, Sqlite>, + { + conn.register_aggregate_function::<#arg_type, #return_type, _, _, A>(#sql_name) + } + }; + } + _ => (), + } + } + } else { + tokens = quote! { + #tokens + + #[derive(ValidGrouping)] + pub struct __Derived<#(#arg_name,)*>(#(#arg_name,)*); + + impl #impl_generics_internal ValidGrouping<__DieselInternal> + for #fn_name #ty_generics + where + __Derived<#(#arg_name,)*>: ValidGrouping<__DieselInternal>, + { + type IsAggregate = <__Derived<#(#arg_name,)*> as ValidGrouping<__DieselInternal>>::IsAggregate; + } + }; + + if cfg!(feature = "sqlite") && type_args.is_empty() && !arg_name.is_empty() { + tokens = quote! { + #tokens + + use diesel::sqlite::{Sqlite, SqliteConnection}; + use diesel::serialize::ToSql; + use diesel::deserialize::{FromSqlRow, StaticallySizedRow}; + + #[allow(dead_code)] + /// Registers an implementation for this function on the given connection + /// + /// This function must be called for every `SqliteConnection` before + /// this SQL function can be used on SQLite. The implementation must be + /// deterministic (returns the same result given the same arguments). If + /// the function is nondeterministic, call + /// `register_nondeterministic_impl` instead. + pub fn register_impl( + conn: &SqliteConnection, + f: F, + ) -> QueryResult<()> + where + F: Fn(#(#arg_name,)*) -> Ret + Send + 'static, + (#(#arg_name,)*): FromSqlRow<(#(#arg_type,)*), Sqlite> + + StaticallySizedRow<(#(#arg_type,)*), Sqlite>, + Ret: ToSql<#return_type, Sqlite>, + { + conn.register_sql_function::<(#(#arg_type,)*), #return_type, _, _, _>( + #sql_name, + true, + move |(#(#arg_name,)*)| f(#(#arg_name,)*), + ) + } + + #[allow(dead_code)] + /// Registers an implementation for this function on the given connection + /// + /// This function must be called for every `SqliteConnection` before + /// this SQL function can be used on SQLite. + /// `register_nondeterministic_impl` should only be used if your + /// function can return different results with the same arguments (e.g. + /// `random`). If your function is deterministic, you should call + /// `register_impl` instead. + pub fn register_nondeterministic_impl( + conn: &SqliteConnection, + mut f: F, + ) -> QueryResult<()> + where + F: FnMut(#(#arg_name,)*) -> Ret + Send + 'static, + (#(#arg_name,)*): FromSqlRow<(#(#arg_type,)*), Sqlite> + + StaticallySizedRow<(#(#arg_type,)*), Sqlite>, + Ret: ToSql<#return_type, Sqlite>, + { + conn.register_sql_function::<(#(#arg_type,)*), #return_type, _, _, _>( + #sql_name, + false, + move |(#(#arg_name,)*)| f(#(#arg_name,)*), + ) + } + }; + } + } + + let args_iter = args.iter(); + tokens = quote! { + #(#attributes)* + #[allow(non_camel_case_types)] + pub #fn_token #fn_name #impl_generics (#(#args_iter,)*) + -> #fn_name::HelperType #ty_generics + #where_clause + #(#arg_name: ::diesel::expression::AsExpression<#arg_type>,)* + { + #fn_name::#fn_name { + #(#arg_struct_assign,)* + #(#type_args: ::std::marker::PhantomData,)* + } + } + + #[doc(hidden)] + #[allow(non_camel_case_types, non_snake_case, unused_imports)] + pub(crate) mod #fn_name { + #tokens + } + }; + + Ok(tokens) +} + +pub(crate) struct SqlFunctionDecl { + attributes: Vec, + fn_token: Token![fn], + fn_name: syn::Ident, + generics: syn::Generics, + args: Punctuated, + return_type: syn::Type, +} + +impl Parse for SqlFunctionDecl { + fn parse(input: ParseStream) -> parse::Result { + let attributes = syn::Attribute::parse_outer(input)?; + let fn_token: Token![fn] = input.parse()?; + let fn_name = syn::Ident::parse(input)?; + let generics = syn::Generics::parse(input)?; + let args; + let _paren = parenthesized!(args in input); + let args = args.parse_terminated::<_, Token![,]>(StrictFnArg::parse)?; + let return_type = if Option::]>::parse(input)?.is_some() { + syn::Type::parse(input)? + } else { + parse_quote!(diesel::expression::expression_types::NotSelectable) + }; + let _semi = Option::::parse(input)?; + + Ok(Self { + attributes, + fn_token, + fn_name, + generics, + args, + return_type, + }) + } +} + +/// Essentially the same as syn::ArgCaptured, but only allowing ident patterns +struct StrictFnArg { + name: syn::Ident, + colon_token: Token![:], + ty: syn::Type, +} + +impl Parse for StrictFnArg { + fn parse(input: ParseStream) -> parse::Result { + let name = input.parse()?; + let colon_token = input.parse()?; + let ty = input.parse()?; + Ok(Self { + name, + colon_token, + ty, + }) + } +} + +impl ToTokens for StrictFnArg { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.name.to_tokens(tokens); + self.colon_token.to_tokens(tokens); + self.name.to_tokens(tokens); + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/sql_type.rs b/collector/benchmarks/diesel/diesel_derives/src/sql_type.rs new file mode 100644 index 000000000..2f8ea7a2b --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/sql_type.rs @@ -0,0 +1,159 @@ +use proc_macro2; +use syn; + +use meta::*; +use util::*; + +pub fn derive(item: syn::DeriveInput) -> Result { + let struct_name = &item.ident; + let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); + + let sqlite_tokens = sqlite_tokens(&item); + let mysql_tokens = mysql_tokens(&item); + let pg_tokens = pg_tokens(&item); + + Ok(wrap_in_dummy_mod(quote! { + impl #impl_generics diesel::sql_types::SqlType + for #struct_name #ty_generics + #where_clause + { + type IsNull = diesel::sql_types::is_nullable::NotNull; + } + + impl #impl_generics diesel::sql_types::SingleValue + for #struct_name #ty_generics + #where_clause + { + } + + #sqlite_tokens + #mysql_tokens + #pg_tokens + })) +} + +fn sqlite_tokens(item: &syn::DeriveInput) -> Option { + MetaItem::with_name(&item.attrs, "sqlite_type") + .map(|attr| attr.expect_ident_value()) + .and_then(|ty| { + if cfg!(not(feature = "sqlite")) { + return None; + } + + let struct_name = &item.ident; + let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); + + Some(quote! { + impl #impl_generics diesel::sql_types::HasSqlType<#struct_name #ty_generics> + for diesel::sqlite::Sqlite + #where_clause + { + fn metadata(_: &()) -> diesel::sqlite::SqliteType { + diesel::sqlite::SqliteType::#ty + } + } + }) + }) +} + +fn mysql_tokens(item: &syn::DeriveInput) -> Option { + MetaItem::with_name(&item.attrs, "mysql_type") + .map(|attr| attr.expect_ident_value()) + .and_then(|ty| { + if cfg!(not(feature = "mysql")) { + return None; + } + + let struct_name = &item.ident; + let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); + + Some(quote! { + impl #impl_generics diesel::sql_types::HasSqlType<#struct_name #ty_generics> + for diesel::mysql::Mysql + #where_clause + { + fn metadata(_: &()) -> diesel::mysql::MysqlType { + diesel::mysql::MysqlType::#ty + } + } + }) + }) +} + +fn pg_tokens(item: &syn::DeriveInput) -> Option { + MetaItem::with_name(&item.attrs, "postgres") + .map(|attr| { + if let Some(x) = get_type_name(&attr)? { + Ok(x) + } else if let Some(x) = get_oids(&attr)? { + Ok(x) + } else { + Err(attr + .span() + .error("Missing required options") + .help("Valid options are `type_name` or `oid` and `array_oid`")) + } + }) + .and_then(|res| res.map_err(Diagnostic::emit).ok()) + .and_then(|ty| { + if cfg!(not(feature = "postgres")) { + return None; + } + + let struct_name = &item.ident; + let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); + + let metadata_fn = match ty { + PgType::Fixed { oid, array_oid } => quote!( + fn metadata(_: &PgMetadataLookup) -> PgTypeMetadata { + PgTypeMetadata { + oid: #oid, + array_oid: #array_oid, + } + } + ), + PgType::Lookup(type_name) => quote!( + fn metadata(lookup: &PgMetadataLookup) -> PgTypeMetadata { + lookup.lookup_type(#type_name) + } + ), + }; + + Some(quote! { + use diesel::pg::{PgMetadataLookup, PgTypeMetadata}; + + impl #impl_generics diesel::sql_types::HasSqlType<#struct_name #ty_generics> + for diesel::pg::Pg + #where_clause + { + #metadata_fn + } + }) + }) +} + +fn get_type_name(attr: &MetaItem) -> Result, Diagnostic> { + Ok(attr.nested_item("type_name")?.map(|ty| { + attr.warn_if_other_options(&["type_name"]); + PgType::Lookup(ty.expect_str_value()) + })) +} + +fn get_oids(attr: &MetaItem) -> Result, Diagnostic> { + if let Some(oid) = attr.nested_item("oid")? { + attr.warn_if_other_options(&["oid", "array_oid"]); + let array_oid = attr.required_nested_item("array_oid")?.expect_int_value(); + let oid = oid.expect_int_value(); + Ok(Some(PgType::Fixed { + oid: oid as u32, + array_oid: array_oid as u32, + })) + } else { + Ok(None) + } +} + +enum PgType { + Fixed { oid: u32, array_oid: u32 }, + Lookup(String), +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/util.rs b/collector/benchmarks/diesel/diesel_derives/src/util.rs new file mode 100644 index 000000000..057a03bec --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/util.rs @@ -0,0 +1,85 @@ +pub use diagnostic_shim::{Diagnostic, DiagnosticShim, EmitErrorExt}; + +use meta::MetaItem; +use proc_macro2::{Span, TokenStream}; +use syn::{Data, DeriveInput, GenericArgument, Type}; + +pub fn wrap_in_dummy_mod(item: TokenStream) -> TokenStream { + quote! { + #[allow(unused_imports)] + const _: () = { + // This import is not actually redundant. When using diesel_derives + // inside of diesel, `diesel` doesn't exist as an extern crate, and + // to work around that it contains a private + // `mod diesel { pub use super::*; }` that this import will then + // refer to. In all other cases, this imports refers to the extern + // crate diesel. + use diesel; + + #item + }; + } +} + +pub fn inner_of_option_ty(ty: &Type) -> &Type { + option_ty_arg(ty).unwrap_or(ty) +} + +pub fn is_option_ty(ty: &Type) -> bool { + option_ty_arg(ty).is_some() +} + +fn option_ty_arg(ty: &Type) -> Option<&Type> { + use syn::PathArguments::AngleBracketed; + + match *ty { + Type::Path(ref ty) => { + let last_segment = ty.path.segments.iter().last().unwrap(); + match last_segment.arguments { + AngleBracketed(ref args) if last_segment.ident == "Option" => { + match args.args.iter().last() { + Some(&GenericArgument::Type(ref ty)) => Some(ty), + _ => None, + } + } + _ => None, + } + } + _ => None, + } +} + +pub fn ty_for_foreign_derive(item: &DeriveInput, flags: &MetaItem) -> Result { + if flags.has_flag("foreign_derive") { + match item.data { + Data::Struct(ref body) => match body.fields.iter().next() { + Some(field) => Ok(field.ty.clone()), + None => Err(flags + .span() + .error("foreign_derive requires at least one field")), + }, + _ => Err(flags + .span() + .error("foreign_derive can only be used with structs")), + } + } else { + let ident = &item.ident; + let (_, ty_generics, ..) = item.generics.split_for_impl(); + Ok(parse_quote!(#ident #ty_generics)) + } +} + +pub fn fix_span(maybe_bad_span: Span, mut fallback: Span) -> Span { + let bad_span_debug = "#0 bytes(0..0)"; + + if format!("{:?}", fallback) == bad_span_debug { + // On recent rust nightlies, even our fallback span is bad. + fallback = Span::call_site(); + } + + if format!("{:?}", maybe_bad_span) == bad_span_debug { + fallback + } else { + maybe_bad_span + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/valid_grouping.rs b/collector/benchmarks/diesel/diesel_derives/src/valid_grouping.rs new file mode 100644 index 000000000..45ec153d0 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/valid_grouping.rs @@ -0,0 +1,69 @@ +use proc_macro2::*; +use syn; + +use meta::*; +use util::*; + +pub fn derive(mut item: syn::DeriveInput) -> Result { + let flags = + MetaItem::with_name(&item.attrs, "diesel").unwrap_or_else(|| MetaItem::empty("diesel")); + let struct_ty = ty_for_foreign_derive(&item, &flags)?; + let type_params = item + .generics + .type_params() + .map(|param| param.ident.clone()) + .collect::>(); + for type_param in type_params { + let where_clause = item.generics.make_where_clause(); + where_clause + .predicates + .push(parse_quote!(#type_param: ValidGrouping<__GroupByClause>)); + } + + let is_aggregate = flags.has_flag("aggregate"); + + if is_aggregate { + item.generics.params.push(parse_quote!(__GroupByClause)); + let (impl_generics, _, where_clause) = item.generics.split_for_impl(); + Ok(wrap_in_dummy_mod(quote! { + use diesel::expression::{ValidGrouping, MixedAggregates, is_aggregate}; + + impl #impl_generics ValidGrouping<__GroupByClause> for #struct_ty + #where_clause + { + type IsAggregate = is_aggregate::Yes; + } + })) + } else { + let mut aggregates = item + .generics + .type_params() + .map(|t| parse_quote!(#t::IsAggregate)) + .collect::>() + .into_iter(); + let is_aggregate = aggregates + .next() + .map(|first| { + let where_clause = item.generics.make_where_clause(); + aggregates.fold(first, |left, right| { + where_clause.predicates.push(parse_quote!( + #left: MixedAggregates<#right> + )); + parse_quote!(<#left as MixedAggregates<#right>>::Output) + }) + }) + .unwrap_or_else(|| parse_quote!(is_aggregate::Never)); + item.generics.params.push(parse_quote!(__GroupByClause)); + let (impl_generics, _, where_clause) = item.generics.split_for_impl(); + + Ok(wrap_in_dummy_mod(quote! { + use diesel::expression::{ValidGrouping, MixedAggregates, is_aggregate}; + + impl #impl_generics ValidGrouping<__GroupByClause> for #struct_ty + #where_clause + { + type IsAggregate = #is_aggregate; + } + })) + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/tests/as_changeset.rs b/collector/benchmarks/diesel/diesel_derives/tests/as_changeset.rs new file mode 100644 index 000000000..e00975100 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/tests/as_changeset.rs @@ -0,0 +1,449 @@ +use diesel::*; +use helpers::*; +use schema::*; + +#[test] +fn named_ref_struct() { + #[derive(AsChangeset)] + struct User { + name: String, + hair_color: String, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&User { + name: String::from("Jim"), + hair_color: String::from("blue"), + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn named_struct() { + #[derive(AsChangeset)] + struct User { + name: String, + hair_color: String, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(User { + name: String::from("Jim"), + hair_color: String::from("blue"), + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn with_explicit_table_name() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm { + name: String, + hair_color: String, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + name: String::from("Jim"), + hair_color: String::from("blue"), + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn with_lifetime() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a> { + name: &'a str, + hair_color: &'a str, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + name: "Jim", + hair_color: "blue", + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn with_multiple_lifetimes() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a, 'b> { + name: &'a str, + hair_color: &'b str, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + name: "Jim", + hair_color: "blue", + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn with_lifetime_constraints() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a, 'b: 'a> { + name: &'a str, + hair_color: &'b str, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + name: "Jim", + hair_color: "blue", + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn with_explicit_column_names() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a> { + #[column_name = "name"] + nombre: &'a str, + #[column_name = "hair_color"] + color_de_pelo: &'a str, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + nombre: "Jim", + color_de_pelo: "blue", + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn tuple_struct() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a>( + #[column_name = "name"] &'a str, + #[column_name = "hair_color"] &'a str, + ); + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm("Jim", "blue")) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn struct_containing_single_field() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a> { + name: &'a str, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { name: "Jim" }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("black"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn tuple_struct_containing_single_field() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a>(#[column_name = "name"] &'a str); + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm("Jim")) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("black"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn primary_key_is_not_updated() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a> { + #[allow(dead_code)] + id: i32, + name: &'a str, + hair_color: &'a str, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + id: 3, + name: "Jim", + hair_color: "blue", + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn primary_key_is_based_on_column_name() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a> { + #[column_name = "id"] + _id: i32, + name: &'a str, + hair_color: &'a str, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + _id: 3, + name: "Jim", + hair_color: "blue", + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn primary_key_is_not_updated_with_custom_pk() { + #[derive(AsChangeset)] + #[table_name = "users"] + #[primary_key(name)] + struct UserForm<'a> { + #[allow(dead_code)] + name: &'a str, + hair_color: &'a str, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + name: "Jim", + hair_color: "blue", + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Sean"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn primary_key_is_not_updated_with_custom_composite_pk() { + #[derive(AsChangeset)] + #[table_name = "users"] + #[primary_key(id, name)] + #[allow(dead_code)] + struct UserForm<'a> { + id: i32, + name: &'a str, + hair_color: &'a str, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + id: 3, + name: "Jim", + hair_color: "blue", + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Sean"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn option_fields_are_skipped() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a> { + name: &'a str, + hair_color: Option<&'a str>, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + name: "Jim", + hair_color: Some("blue"), + }) + .execute(&connection) + .unwrap(); + update(users::table.find(2)) + .set(&UserForm { + name: "Ruby", + hair_color: None, + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Ruby"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn option_fields_are_assigned_null_when_specified() { + #[derive(AsChangeset)] + #[table_name = "users"] + #[changeset_options(treat_none_as_null = "true")] + struct UserForm<'a> { + name: &'a str, + hair_color: Option<&'a str>, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + name: "Jim", + hair_color: Some("blue"), + }) + .execute(&connection) + .unwrap(); + update(users::table.find(2)) + .set(&UserForm { + name: "Ruby", + hair_color: None, + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Ruby"), None), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} diff --git a/collector/benchmarks/diesel/diesel_derives/tests/associations.rs b/collector/benchmarks/diesel/diesel_derives/tests/associations.rs new file mode 100644 index 000000000..c428ded99 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/tests/associations.rs @@ -0,0 +1,293 @@ +use diesel::*; +use helpers::*; + +type Backend = ::Backend; + +#[test] +fn simple_belongs_to() { + table! { + users { + id -> Integer, + name -> Text, + } + } + + table! { + posts { + id -> Integer, + user_id -> Integer, + title -> Text, + } + } + + allow_tables_to_appear_in_same_query!(users, posts); + + #[derive(Identifiable)] + pub struct User { + id: i32, + } + + #[derive(Associations, Identifiable)] + #[belongs_to(User)] + pub struct Post { + id: i32, + user_id: i32, + } + + joinable!(posts -> users(user_id)); + + let _can_join_tables = posts::table + .inner_join(users::table) + .select((users::id, users::name, posts::id)) + .filter( + posts::id + .eq(1) + .and(posts::user_id.eq(2)) + .and(posts::title.eq("Bar")), + ); + + let _can_reverse_join_tables = users::table + .inner_join(posts::table) + .select((posts::id, posts::user_id, posts::title)) + .filter(users::id.eq(1).and(users::name.eq("Sean"))); + + let t = User { id: 42 }; + + let belong_to = Post::belonging_to(&t); + let filter = posts::table.filter(posts::user_id.eq(42)); + + assert_eq!( + debug_query::(&belong_to).to_string(), + debug_query::(&filter).to_string() + ); +} + +#[test] +fn custom_foreign_key() { + table! { + users { + id -> Integer, + name -> Text, + } + } + + table! { + posts { + id -> Integer, + belongs_to_user -> Integer, + title -> Text, + } + } + + allow_tables_to_appear_in_same_query!(users, posts); + + #[derive(Identifiable)] + pub struct User { + id: i32, + } + + #[derive(Associations, Identifiable)] + #[belongs_to(User, foreign_key = "belongs_to_user")] + pub struct Post { + id: i32, + belongs_to_user: i32, + } + + joinable!(posts -> users(belongs_to_user)); + + let _can_join_tables = posts::table + .inner_join(users::table) + .select((users::id, users::name)) + .filter( + posts::id + .eq(1) + .and(posts::belongs_to_user.eq(2)) + .and(posts::title.eq("Bar")), + ); + + let _can_reverse_join_tables = users::table + .inner_join(posts::table) + .select((posts::id, posts::belongs_to_user, posts::title)) + .filter(users::id.eq(1).and(users::name.eq("Sean"))); + + let t = User { id: 42 }; + + let belong_to = Post::belonging_to(&t); + let filter = posts::table.filter(posts::belongs_to_user.eq(42)); + + assert_eq!( + debug_query::(&belong_to).to_string(), + debug_query::(&filter).to_string() + ); +} + +#[test] +fn self_referential() { + table! { + trees { + id -> Integer, + parent_id -> Nullable, + } + } + + #[derive(Associations, Identifiable)] + #[belongs_to(Tree, foreign_key = "parent_id")] + pub struct Tree { + id: i32, + parent_id: Option, + } + let t = Tree { + id: 42, + parent_id: None, + }; + + let belong_to = Tree::belonging_to(&t); + let filter = trees::table.filter(trees::parent_id.eq(42)); + assert_eq!( + debug_query::(&belong_to).to_string(), + debug_query::(&filter).to_string() + ); +} + +#[test] +fn multiple_associations() { + table! { + users { + id -> Integer, + } + } + + table! { + posts { + id -> Integer, + } + } + + table! { + comments { + id -> Integer, + user_id -> Integer, + post_id -> Integer, + } + } + + #[derive(Identifiable)] + struct User { + id: i32, + } + + #[derive(Identifiable)] + struct Post { + id: i32, + } + + #[derive(Identifiable, Associations)] + #[belongs_to(User)] + #[belongs_to(Post)] + struct Comment { + id: i32, + user_id: i32, + post_id: i32, + } + + let user = User { id: 1 }; + let post = Post { id: 2 }; + + let query = Comment::belonging_to(&user); + let expected = comments::table.filter(comments::user_id.eq(1)); + assert_eq!( + debug_query::(&query).to_string(), + debug_query::(&expected).to_string() + ); + let query = Comment::belonging_to(&post); + let expected = comments::table.filter(comments::post_id.eq(2)); + assert_eq!( + debug_query::(&query).to_string(), + debug_query::(&expected).to_string() + ); +} + +#[test] +fn foreign_key_field_with_column_rename() { + table! { + users { + id -> Integer, + } + } + + table! { + posts { + id -> Integer, + user_id -> Integer, + } + } + + #[derive(Identifiable, Clone, Copy)] + pub struct User { + id: i32, + } + + #[derive(Associations, Identifiable, Clone, Copy, PartialEq, Debug)] + #[belongs_to(User)] + pub struct Post { + id: i32, + #[column_name = "user_id"] + author_id: i32, + } + + let user1 = User { id: 1 }; + let user2 = User { id: 2 }; + let post1 = Post { + id: 1, + author_id: 2, + }; + let post2 = Post { + id: 2, + author_id: 1, + }; + + let query = Post::belonging_to(&user1); + let expected = posts::table.filter(posts::user_id.eq(1)); + assert_eq!( + debug_query::(&query).to_string(), + debug_query::(&expected).to_string() + ); + + let users = vec![user1, user2]; + let posts = vec![post1, post2].grouped_by(&users); + assert_eq!(vec![vec![post2], vec![post1]], posts); +} + +#[test] +fn tuple_struct() { + table! { + users { + id -> Integer, + } + } + + table! { + posts { + id -> Integer, + user_id -> Integer, + } + } + + #[derive(Identifiable)] + pub struct User { + id: i32, + } + + #[derive(Associations, Identifiable)] + #[belongs_to(User)] + pub struct Post(#[column_name = "id"] i32, #[column_name = "user_id"] i32); + + let user = User { id: 1 }; + + let query = Post::belonging_to(&user); + let expected = posts::table.filter(posts::user_id.eq(1)); + assert_eq!( + debug_query::(&query).to_string(), + debug_query::(&expected).to_string() + ); +} diff --git a/collector/benchmarks/diesel/diesel_derives/tests/helpers.rs b/collector/benchmarks/diesel/diesel_derives/tests/helpers.rs new file mode 100644 index 000000000..d2cb5d483 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/tests/helpers.rs @@ -0,0 +1,80 @@ +use diesel::prelude::*; +use diesel::sql_query; + +cfg_if! { + if #[cfg(feature = "sqlite")] { + pub type TestConnection = SqliteConnection; + + pub fn connection() -> TestConnection { + let conn = SqliteConnection::establish(":memory:").unwrap(); + sql_query("CREATE TABLE users (\ + id INTEGER PRIMARY KEY AUTOINCREMENT, \ + name VARCHAR NOT NULL, \ + hair_color VARCHAR DEFAULT 'Green')") + .execute(&conn) + .unwrap(); + conn + } + } else if #[cfg(feature = "postgres")] { + extern crate dotenv; + + pub type TestConnection = PgConnection; + + pub fn connection() -> TestConnection { + let database_url = dotenv::var("PG_DATABASE_URL") + .or_else(|_| dotenv::var("DATABASE_URL")) + .expect("DATABASE_URL must be set in order to run tests"); + let conn = PgConnection::establish(&database_url).unwrap(); + conn.begin_test_transaction().unwrap(); + sql_query("DROP TABLE IF EXISTS users CASCADE").execute(&conn).unwrap(); + sql_query("CREATE TABLE users (\ + id SERIAL PRIMARY KEY, \ + name VARCHAR NOT NULL, \ + hair_color VARCHAR DEFAULT 'Green')") + .execute(&conn) + .unwrap(); + conn + } + } else if #[cfg(feature = "mysql")] { + extern crate dotenv; + + pub type TestConnection = MysqlConnection; + + pub fn connection() -> TestConnection { + let database_url = dotenv::var("MYSQL_UNIT_TEST_DATABASE_URL") + .or_else(|_| dotenv::var("DATABASE_URL")) + .expect("DATABASE_URL must be set in order to run tests"); + let conn = MysqlConnection::establish(&database_url).unwrap(); + sql_query("DROP TABLE IF EXISTS users CASCADE").execute(&conn).unwrap(); + sql_query("CREATE TABLE users (\ + id INTEGER PRIMARY KEY AUTO_INCREMENT, \ + name TEXT NOT NULL, \ + hair_color VARCHAR(255) DEFAULT 'Green')") + .execute(&conn) + .unwrap(); + conn.begin_test_transaction().unwrap(); + conn + } + } else { + compile_error!( + "At least one backend must be used to test this crate.\n \ + Pass argument `--features \"\"` with one or more of the following backends, \ + 'mysql', 'postgres', or 'sqlite'. \n\n \ + ex. cargo test --features \"mysql postgres sqlite\"\n" + ); + } +} + +pub fn connection_with_sean_and_tess_in_users_table() -> TestConnection { + use schema::users::dsl::*; + + let connection = connection(); + ::diesel::insert_into(users) + .values(&vec![ + (id.eq(1), name.eq("Sean"), hair_color.eq("black")), + (id.eq(2), name.eq("Tess"), hair_color.eq("brown")), + ]) + .execute(&connection) + .unwrap(); + connection +} diff --git a/collector/benchmarks/diesel/diesel_derives/tests/identifiable.rs b/collector/benchmarks/diesel/diesel_derives/tests/identifiable.rs new file mode 100644 index 000000000..ea912f951 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/tests/identifiable.rs @@ -0,0 +1,152 @@ +use diesel::associations::Identifiable; + +table! { + foos { + id -> Integer, + } +} + +table! { + bars { + id -> VarChar, + } +} + +#[test] +fn derive_identifiable_on_simple_struct() { + #[derive(Identifiable)] + struct Foo { + id: i32, + #[allow(dead_code)] + foo: i32, + } + + let foo1 = Foo { id: 1, foo: 2 }; + let foo2 = Foo { id: 2, foo: 3 }; + assert_eq!(&1, foo1.id()); + assert_eq!(&2, foo2.id()); +} + +#[test] +fn derive_identifiable_on_tuple_struct() { + #[derive(Identifiable)] + struct Foo( + #[column_name = "id"] i32, + #[allow(dead_code)] + #[column_name = "lol"] + i32, + ); + + let foo1 = Foo(1, 2); + let foo2 = Foo(2, 3); + assert_eq!(&1, foo1.id()); + assert_eq!(&2, foo2.id()); +} + +#[test] +fn derive_identifiable_when_id_is_not_first_field() { + #[derive(Identifiable)] + struct Foo { + #[allow(dead_code)] + foo: i32, + id: i32, + } + + let foo1 = Foo { id: 1, foo: 2 }; + let foo2 = Foo { id: 2, foo: 3 }; + assert_eq!(&1, foo1.id()); + assert_eq!(&2, foo2.id()); +} + +#[test] +fn derive_identifiable_on_struct_with_non_integer_pk() { + #[derive(Identifiable)] + #[table_name = "bars"] + struct Foo { + id: &'static str, + #[allow(dead_code)] + foo: i32, + } + + let foo1 = Foo { id: "hi", foo: 2 }; + let foo2 = Foo { + id: "there", + foo: 3, + }; + assert_eq!(&"hi", foo1.id()); + assert_eq!(&"there", foo2.id()); +} + +#[test] +fn derive_identifiable_on_struct_with_lifetime() { + #[derive(Identifiable)] + #[table_name = "bars"] + struct Foo<'a> { + id: &'a str, + #[allow(dead_code)] + foo: i32, + } + + let foo1 = Foo { id: "hi", foo: 2 }; + let foo2 = Foo { + id: "there", + foo: 3, + }; + assert_eq!(&"hi", foo1.id()); + assert_eq!(&"there", foo2.id()); +} + +#[test] +fn derive_identifiable_with_non_standard_pk() { + #[allow(dead_code)] + #[derive(Identifiable)] + #[table_name = "bars"] + #[primary_key(foo_id)] + struct Foo<'a> { + id: i32, + foo_id: &'a str, + foo: i32, + } + + let foo1 = Foo { + id: 1, + foo_id: "hi", + foo: 2, + }; + let foo2 = Foo { + id: 2, + foo_id: "there", + foo: 3, + }; + assert_eq!(&"hi", foo1.id()); + assert_eq!(&"there", foo2.id()); +} + +#[test] +fn derive_identifiable_with_composite_pk() { + #[allow(dead_code)] + #[derive(Identifiable)] + #[table_name = "bars"] + #[primary_key(foo_id, bar_id)] + struct Foo { + id: i32, + foo_id: i32, + bar_id: i32, + foo: i32, + } + + let foo1 = Foo { + id: 1, + foo_id: 2, + bar_id: 3, + foo: 4, + }; + let foo2 = Foo { + id: 5, + foo_id: 6, + bar_id: 7, + foo: 8, + }; + assert_eq!((&2, &3), foo1.id()); + assert_eq!((&6, &7), foo2.id()); +} diff --git a/collector/benchmarks/diesel/diesel_derives/tests/insertable.rs b/collector/benchmarks/diesel/diesel_derives/tests/insertable.rs new file mode 100644 index 000000000..b03e2cbb7 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/tests/insertable.rs @@ -0,0 +1,298 @@ +use diesel::*; +use helpers::*; + +table! { + users { + id -> Integer, + name -> VarChar, + hair_color -> Nullable, + } +} + +#[test] +fn simple_struct_definition() { + #[derive(Insertable)] + #[table_name = "users"] + struct NewUser { + name: String, + hair_color: String, + } + + let conn = connection(); + let new_user = NewUser { + name: "Sean".into(), + hair_color: "Black".into(), + }; + insert_into(users::table) + .values(new_user) + .execute(&conn) + .unwrap(); + + let saved = users::table + .select((users::name, users::hair_color)) + .load::<(String, Option)>(&conn); + let expected = vec![("Sean".to_string(), Some("Black".to_string()))]; + assert_eq!(Ok(expected), saved); +} + +#[test] +fn simple_reference_definition() { + #[derive(Insertable)] + #[table_name = "users"] + struct NewUser { + name: String, + hair_color: String, + } + + let conn = connection(); + let new_user = NewUser { + name: "Sean".into(), + hair_color: "Black".into(), + }; + insert_into(users::table) + .values(&new_user) + .execute(&conn) + .unwrap(); + + let saved = users::table + .select((users::name, users::hair_color)) + .load::<(String, Option)>(&conn); + let expected = vec![("Sean".to_string(), Some("Black".to_string()))]; + assert_eq!(Ok(expected), saved); +} + +macro_rules! test_struct_definition { + ($test_name:ident, $struct_def:item) => { + #[test] + fn $test_name() { + #[derive(Insertable)] + #[table_name = "users"] + $struct_def + + let conn = connection(); + let new_user = NewUser { name: "Sean".into(), hair_color: None }; + insert_into(users::table).values(&new_user).execute(&conn).unwrap(); + + let saved = users::table.select((users::name, users::hair_color)) + .load::<(String, Option)>(&conn); + let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; + assert_eq!(Ok(expected), saved); + } + } +} + +test_struct_definition! { + struct_with_option_field, + struct NewUser { + name: String, + hair_color: Option, + } +} + +test_struct_definition! { + pub_struct_definition, + pub struct NewUser { + name: String, + hair_color: Option, + } +} + +test_struct_definition! { + struct_with_pub_field, + pub struct NewUser { + pub name: String, + hair_color: Option, + } +} + +test_struct_definition! { + struct_with_pub_option_field, + pub struct NewUser { + name: String, + pub hair_color: Option, + } +} + +test_struct_definition! { + named_struct_with_borrowed_body, + struct NewUser<'a> { + name: &'a str, + hair_color: Option<&'a str>, + } +} + +#[test] +fn named_struct_with_renamed_field() { + #[derive(Insertable)] + #[table_name = "users"] + struct NewUser { + #[column_name = "name"] + my_name: String, + hair_color: String, + } + + let conn = connection(); + let new_user = NewUser { + my_name: "Sean".into(), + hair_color: "Black".into(), + }; + insert_into(users::table) + .values(&new_user) + .execute(&conn) + .unwrap(); + + let saved = users::table + .select((users::name, users::hair_color)) + .load::<(String, Option)>(&conn); + let expected = vec![("Sean".to_string(), Some("Black".to_string()))]; + assert_eq!(Ok(expected), saved); +} + +#[test] +fn named_struct_with_renamed_option_field() { + #[derive(Insertable)] + #[table_name = "users"] + struct NewUser { + #[column_name = "name"] + my_name: String, + #[column_name = "hair_color"] + my_hair_color: Option, + } + + let conn = connection(); + let new_user = NewUser { + my_name: "Sean".into(), + my_hair_color: None, + }; + insert_into(users::table) + .values(&new_user) + .execute(&conn) + .unwrap(); + + let saved = users::table + .select((users::name, users::hair_color)) + .load::<(String, Option)>(&conn); + let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; + assert_eq!(Ok(expected), saved); +} + +#[test] +fn tuple_struct() { + #[derive(Insertable)] + #[table_name = "users"] + struct NewUser<'a>( + #[column_name = "name"] &'a str, + #[column_name = "hair_color"] Option<&'a str>, + ); + + let conn = connection(); + let new_user = NewUser("Sean", None); + insert_into(users::table) + .values(&new_user) + .execute(&conn) + .unwrap(); + + let saved = users::table + .select((users::name, users::hair_color)) + .load::<(String, Option)>(&conn); + let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; + assert_eq!(Ok(expected), saved); +} + +#[test] +fn named_struct_with_unusual_reference_type() { + #[derive(Insertable)] + #[table_name = "users"] + struct NewUser<'a> { + name: &'a String, + hair_color: Option<&'a String>, + } + + let conn = connection(); + let sean = "Sean".to_string(); + let black = "Black".to_string(); + let new_user = NewUser { + name: &sean, + hair_color: Some(&black), + }; + insert_into(users::table) + .values(&new_user) + .execute(&conn) + .unwrap(); + + let saved = users::table + .select((users::name, users::hair_color)) + .load(&conn); + let expected = vec![(sean.clone(), Some(black.clone()))]; + assert_eq!(Ok(expected), saved); +} + +#[test] +#[cfg(all(feature = "postgres", not(feature = "sqlite")))] +fn insertable_with_slice_of_borrowed() { + table! { + posts { + id -> Serial, + tags -> Array, + } + } + + #[derive(Insertable)] + #[table_name = "posts"] + struct NewPost<'a> { + tags: &'a [&'a str], + } + + let conn = connection(); + sql_query("DROP TABLE IF EXISTS posts CASCADE") + .execute(&conn) + .unwrap(); + sql_query("CREATE TABLE posts (id SERIAL PRIMARY KEY, tags TEXT[] NOT NULL)") + .execute(&conn) + .unwrap(); + let new_post = NewPost { + tags: &["hi", "there"], + }; + insert_into(posts::table) + .values(&new_post) + .execute(&conn) + .unwrap(); + + let saved = posts::table.select(posts::tags).load::>(&conn); + let expected = vec![vec![String::from("hi"), String::from("there")]]; + assert_eq!(Ok(expected), saved); +} + +#[test] +fn embedded_struct() { + #[derive(Insertable)] + #[table_name = "users"] + struct NameAndHairColor<'a> { + name: &'a str, + hair_color: &'a str, + } + + #[derive(Insertable)] + struct User<'a> { + id: i32, + #[diesel(embed)] + name_and_hair_color: NameAndHairColor<'a>, + } + + let conn = connection(); + let new_user = User { + id: 1, + name_and_hair_color: NameAndHairColor { + name: "Sean", + hair_color: "Black", + }, + }; + insert_into(users::table) + .values(&new_user) + .execute(&conn) + .unwrap(); + + let saved = users::table.load::<(i32, String, Option)>(&conn); + let expected = vec![(1, "Sean".to_string(), Some("Black".to_string()))]; + assert_eq!(Ok(expected), saved); +} diff --git a/collector/benchmarks/diesel/diesel_derives/tests/queryable.rs b/collector/benchmarks/diesel/diesel_derives/tests/queryable.rs new file mode 100644 index 000000000..d30114bb4 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/tests/queryable.rs @@ -0,0 +1,38 @@ +use diesel::dsl::sql; +use diesel::sql_types::Integer; +use diesel::*; + +use helpers::connection; + +#[test] +fn named_struct_definition() { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable)] + struct MyStruct { + foo: i32, + bar: i32, + } + + let conn = connection(); + let data = select(sql::<(Integer, Integer)>("1, 2")).get_result(&conn); + assert_eq!(Ok(MyStruct { foo: 1, bar: 2 }), data); +} + +#[test] +fn tuple_struct() { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable)] + struct MyStruct(#[column_name = "foo"] i32, #[column_name = "bar"] i32); + + let conn = connection(); + let data = select(sql::<(Integer, Integer)>("1, 2")).get_result(&conn); + assert_eq!(Ok(MyStruct(1, 2)), data); +} + +#[test] +fn tuple_struct_without_column_name_annotations() { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable)] + struct MyStruct(i32, i32); + + let conn = connection(); + let data = select(sql::<(Integer, Integer)>("1, 2")).get_result(&conn); + assert_eq!(Ok(MyStruct(1, 2)), data); +} diff --git a/collector/benchmarks/diesel/diesel_derives/tests/queryable_by_name.rs b/collector/benchmarks/diesel/diesel_derives/tests/queryable_by_name.rs new file mode 100644 index 000000000..acddaf4a9 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/tests/queryable_by_name.rs @@ -0,0 +1,109 @@ +use diesel::sql_types::Integer; +use diesel::*; + +use helpers::connection; + +table! { + my_structs (foo) { + foo -> Integer, + bar -> Integer, + } +} + +#[test] +fn named_struct_definition() { + #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] + #[table_name = "my_structs"] + struct MyStruct { + foo: i32, + bar: i32, + } + + let conn = connection(); + let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(&conn); + assert_eq!(Ok(MyStruct { foo: 1, bar: 2 }), data); +} + +#[test] +fn tuple_struct() { + #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] + #[table_name = "my_structs"] + struct MyStruct(#[column_name = "foo"] i32, #[column_name = "bar"] i32); + + let conn = connection(); + let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(&conn); + assert_eq!(Ok(MyStruct(1, 2)), data); +} + +// FIXME: Test usage with renamed columns + +#[test] +fn struct_with_no_table() { + #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] + struct MyStructNamedSoYouCantInferIt { + #[sql_type = "Integer"] + foo: i32, + #[sql_type = "Integer"] + bar: i32, + } + + let conn = connection(); + let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(&conn); + assert_eq!(Ok(MyStructNamedSoYouCantInferIt { foo: 1, bar: 2 }), data); +} + +#[test] +fn embedded_struct() { + #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] + #[table_name = "my_structs"] + struct A { + foo: i32, + #[diesel(embed)] + b: B, + } + + #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] + #[table_name = "my_structs"] + struct B { + bar: i32, + } + + let conn = connection(); + let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(&conn); + assert_eq!( + Ok(A { + foo: 1, + b: B { bar: 2 }, + }), + data + ); +} + +#[test] +fn embedded_option() { + #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] + #[table_name = "my_structs"] + struct A { + foo: i32, + #[diesel(embed)] + b: Option, + } + + #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] + #[table_name = "my_structs"] + struct B { + bar: i32, + } + + let conn = connection(); + let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(&conn); + assert_eq!( + Ok(A { + foo: 1, + b: Some(B { bar: 2 }), + }), + data + ); + let data = sql_query("SELECT 1 AS foo, NULL AS bar").get_result(&conn); + assert_eq!(Ok(A { foo: 1, b: None }), data); +} diff --git a/collector/benchmarks/diesel/diesel_derives/tests/schema.rs b/collector/benchmarks/diesel/diesel_derives/tests/schema.rs new file mode 100644 index 000000000..aa0b96519 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/tests/schema.rs @@ -0,0 +1,7 @@ +table! { + users { + id -> Integer, + name -> Text, + hair_color -> Nullable, + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/tests/tests.rs b/collector/benchmarks/diesel/diesel_derives/tests/tests.rs new file mode 100644 index 000000000..636fea662 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/tests/tests.rs @@ -0,0 +1,16 @@ +#![deny(warnings)] + +#[macro_use] +extern crate cfg_if; +#[macro_use] +extern crate diesel; + +mod helpers; +mod schema; + +mod as_changeset; +mod associations; +mod identifiable; +mod insertable; +mod queryable; +mod queryable_by_name; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170207193805_initial_schema/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170207193805_initial_schema/down.sql new file mode 100644 index 000000000..dc5cd92b9 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170207193805_initial_schema/down.sql @@ -0,0 +1,6 @@ +DROP TABLE followings; +DROP TABLE special_comments; +DROP TABLE special_posts; +DROP TABLE comments; +DROP TABLE posts; +DROP TABLE users; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170207193805_initial_schema/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170207193805_initial_schema/up.sql new file mode 100644 index 000000000..d25a3b9b1 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170207193805_initial_schema/up.sql @@ -0,0 +1,36 @@ +CREATE TABLE users ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + name TEXT NOT NULL, + hair_color TEXT +) CHARACTER SET utf8mb4; + +CREATE TABLE posts ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + user_id INTEGER NOT NULL, + title TEXT NOT NULL, + body TEXT +) CHARACTER SET utf8mb4; + +CREATE TABLE comments ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + post_id INTEGER NOT NULL, + text TEXT NOT NULL +) CHARACTER SET utf8mb4; + +CREATE TABLE special_posts ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + user_id INTEGER NOT NULL, + title TEXT NOT NULL +) CHARACTER SET utf8mb4; + +CREATE TABLE special_comments ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + special_post_id INTEGER NOT NULL +) CHARACTER SET utf8mb4; + +CREATE TABLE followings ( + user_id INTEGER NOT NULL, + post_id INTEGER NOT NULL, + email_notifications TINYINT(1) NOT NULL DEFAULT 0, + PRIMARY KEY (user_id, post_id) +); diff --git a/collector/benchmarks/diesel/migrations/mysql/20170209180355_add_one_off_tables_from_integration_tests/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170209180355_add_one_off_tables_from_integration_tests/down.sql new file mode 100644 index 000000000..db631b629 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170209180355_add_one_off_tables_from_integration_tests/down.sql @@ -0,0 +1,5 @@ +DROP TABLE numbers; +DROP TABLE precision_numbers; +DROP TABLE nullable_doubles; +DROP TABLE users_with_name_pk; +DROP TABLE points; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170209180355_add_one_off_tables_from_integration_tests/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170209180355_add_one_off_tables_from_integration_tests/up.sql new file mode 100644 index 000000000..cacf5aa46 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170209180355_add_one_off_tables_from_integration_tests/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE numbers (n INTEGER PRIMARY KEY); +CREATE TABLE precision_numbers (n DOUBLE PRECISION NOT NULL PRIMARY KEY); +CREATE TABLE nullable_doubles (id INT PRIMARY KEY AUTO_INCREMENT, n DOUBLE PRECISION); +CREATE TABLE users_with_name_pk (name VARCHAR(50) PRIMARY KEY); +CREATE TABLE points (x INTEGER NOT NULL, y INTEGER NOT NULL, PRIMARY KEY (x, y)); diff --git a/collector/benchmarks/diesel/migrations/mysql/20170211150830_index_columns_used_in_benchmarks/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170211150830_index_columns_used_in_benchmarks/down.sql new file mode 100644 index 000000000..c19dc97fe --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170211150830_index_columns_used_in_benchmarks/down.sql @@ -0,0 +1,2 @@ +DROP INDEX users_hair_color; +DROP INDEX posts_user_id; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170211150830_index_columns_used_in_benchmarks/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170211150830_index_columns_used_in_benchmarks/up.sql new file mode 100644 index 000000000..018068a3d --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170211150830_index_columns_used_in_benchmarks/up.sql @@ -0,0 +1,2 @@ +CREATE INDEX users_hair_color ON users (hair_color(255)); +CREATE INDEX posts_user_id ON posts (user_id); diff --git a/collector/benchmarks/diesel/migrations/mysql/20170215170122_create_trees/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170215170122_create_trees/down.sql new file mode 100644 index 000000000..1135e76a6 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170215170122_create_trees/down.sql @@ -0,0 +1 @@ +DROP TABLE trees; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170215170122_create_trees/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170215170122_create_trees/up.sql new file mode 100644 index 000000000..4e0db46f3 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170215170122_create_trees/up.sql @@ -0,0 +1,4 @@ +CREATE TABLE trees ( + id INTEGER PRIMARY KEY, + parent_id INTEGER +); diff --git a/collector/benchmarks/diesel/migrations/mysql/20170219200159_add_foreign_key/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170219200159_add_foreign_key/down.sql new file mode 100644 index 000000000..9dea4d73a --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170219200159_add_foreign_key/down.sql @@ -0,0 +1,2 @@ +DROP TABLE fk_tests; +DROP TABLE fk_inits; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170219200159_add_foreign_key/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170219200159_add_foreign_key/up.sql new file mode 100644 index 000000000..c1df0ccd1 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170219200159_add_foreign_key/up.sql @@ -0,0 +1,9 @@ +CREATE TABLE fk_inits ( + id INTEGER PRIMARY KEY +); + +CREATE TABLE fk_tests ( + id INTEGER PRIMARY KEY, + fk_id INTEGER NOT NULL, + FOREIGN KEY (fk_id) REFERENCES fk_inits (id) +); diff --git a/collector/benchmarks/diesel/migrations/mysql/20170407152306_add_nullable_table/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170407152306_add_nullable_table/down.sql new file mode 100644 index 000000000..ff84418a4 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170407152306_add_nullable_table/down.sql @@ -0,0 +1 @@ +DROP TABLE nullable_table; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170407152306_add_nullable_table/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170407152306_add_nullable_table/up.sql new file mode 100644 index 000000000..74da9a32f --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170407152306_add_nullable_table/up.sql @@ -0,0 +1,4 @@ +CREATE TABLE nullable_table ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + value INTEGER +) CHARACTER SET utf8mb4; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170603131224_add_likes/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170603131224_add_likes/down.sql new file mode 100644 index 000000000..f7c67fce5 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170603131224_add_likes/down.sql @@ -0,0 +1 @@ +DROP TABLE likes; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170603131224_add_likes/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170603131224_add_likes/up.sql new file mode 100644 index 000000000..cb37d8d14 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170603131224_add_likes/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE likes ( + comment_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + PRIMARY KEY (comment_id, user_id) +); diff --git a/collector/benchmarks/diesel/migrations/mysql/20170804172328_add_foreign_keys/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170804172328_add_foreign_keys/down.sql new file mode 100644 index 000000000..03a1d1831 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170804172328_add_foreign_keys/down.sql @@ -0,0 +1,6 @@ +ALTER TABLE posts DROP FOREIGN KEY posts_user_id_fkey; +ALTER TABLE comments DROP FOREIGN KEY comments_post_id_fkey; +ALTER TABLE followings DROP FOREIGN KEY followings_user_id_fkey; +ALTER TABLE followings DROP FOREIGN KEY followings_post_id_fkey; +ALTER TABLE likes DROP FOREIGN KEY likes_comment_id_fkey; +ALTER TABLE likes DROP FOREIGN KEY likes_user_id_fkey; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170804172328_add_foreign_keys/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170804172328_add_foreign_keys/up.sql new file mode 100644 index 000000000..dee94ba36 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170804172328_add_foreign_keys/up.sql @@ -0,0 +1,6 @@ +ALTER TABLE posts ADD CONSTRAINT posts_user_id_fkey FOREIGN KEY (user_id) REFERENCES users (id); +ALTER TABLE comments ADD CONSTRAINT comments_post_id_fkey FOREIGN KEY (post_id) REFERENCES posts (id); +ALTER TABLE followings ADD CONSTRAINT followings_user_id_fkey FOREIGN KEY (user_id) REFERENCES users (id); +ALTER TABLE followings ADD CONSTRAINT followings_post_id_fkey FOREIGN KEY (post_id) REFERENCES posts (id); +ALTER TABLE likes ADD CONSTRAINT likes_comment_id_fkey FOREIGN KEY (comment_id) REFERENCES comments (id); +ALTER TABLE likes ADD CONSTRAINT likes_user_id_fkey FOREIGN KEY (user_id) REFERENCES users (id); diff --git a/collector/benchmarks/diesel/migrations/mysql/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql new file mode 100644 index 000000000..568c8ed82 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql @@ -0,0 +1,6 @@ +DROP TABLE self_referential_fk; +DROP TABLE fk_doesnt_reference_pk; +DROP TABLE composite_fk; +DROP TABLE multiple_fks_to_same_table; +DROP TABLE cyclic_fk_1 CASCADE; +DROP TABLE cyclic_fk_2 CASCADE; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql new file mode 100644 index 000000000..2cfc789c0 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql @@ -0,0 +1,36 @@ +CREATE TABLE self_referential_fk ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + parent_id INTEGER NOT NULL +); + +ALTER TABLE self_referential_fk ADD CONSTRAINT self_referential_fk_parent_id_fk FOREIGN KEY (parent_id) REFERENCES self_referential_fk (id); + +CREATE TABLE fk_doesnt_reference_pk ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + random TEXT REFERENCES posts (title) +); + +CREATE TABLE composite_fk ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + post_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + FOREIGN KEY (user_id, post_id) REFERENCES followings (user_id, post_id) +); + +CREATE TABLE multiple_fks_to_same_table ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + post_id_1 INTEGER REFERENCES posts, + post_id_2 INTEGER REFERENCES posts +); + +CREATE TABLE cyclic_fk_1 ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + cyclic_fk_2_id INTEGER +); + +CREATE TABLE cyclic_fk_2 ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + cyclic_fk_1_id INTEGER REFERENCES cyclic_fk_1 (id) +); + +ALTER TABLE cyclic_fk_1 ADD CONSTRAINT cyclic_fk_1_cyclic_fk_2_id_fk FOREIGN KEY (cyclic_fk_2_id) REFERENCES cyclic_fk_2 (id); diff --git a/collector/benchmarks/diesel/migrations/mysql/20170811104602_add_blob_types/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170811104602_add_blob_types/down.sql new file mode 100644 index 000000000..d322f8997 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170811104602_add_blob_types/down.sql @@ -0,0 +1 @@ +DROP TABLE all_the_blobs; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170811104602_add_blob_types/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170811104602_add_blob_types/up.sql new file mode 100644 index 000000000..cb9a934e4 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170811104602_add_blob_types/up.sql @@ -0,0 +1,7 @@ +CREATE TABLE all_the_blobs ( + id INTEGER PRIMARY KEY, -- Can't use a blob as a pk + tiny TINYBLOB NOT NULL, + normal BLOB NOT NULL, + medium MEDIUMBLOB NOT NULL, + big LONGBLOB NOT NULL +) diff --git a/collector/benchmarks/diesel/migrations/mysql/20170816164349_add_keywords_table/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170816164349_add_keywords_table/down.sql new file mode 100644 index 000000000..20296cf53 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170816164349_add_keywords_table/down.sql @@ -0,0 +1 @@ +DROP TABLE with_keywords; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170816164349_add_keywords_table/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170816164349_add_keywords_table/up.sql new file mode 100644 index 000000000..fec19b939 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170816164349_add_keywords_table/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE with_keywords ( + fn INTEGER NOT NULL PRIMARY KEY, + let INTEGER NOT NULL, + extern INTEGER NOT NULL +); diff --git a/collector/benchmarks/diesel/migrations/mysql/2020-01-25-033332_add unsigned table/down.sql b/collector/benchmarks/diesel/migrations/mysql/2020-01-25-033332_add unsigned table/down.sql new file mode 100644 index 000000000..8ebd48e8d --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/2020-01-25-033332_add unsigned table/down.sql @@ -0,0 +1 @@ +DROP TABLE unsigned_table; \ No newline at end of file diff --git a/collector/benchmarks/diesel/migrations/mysql/2020-01-25-033332_add unsigned table/up.sql b/collector/benchmarks/diesel/migrations/mysql/2020-01-25-033332_add unsigned table/up.sql new file mode 100644 index 000000000..00f52b32e --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/2020-01-25-033332_add unsigned table/up.sql @@ -0,0 +1,4 @@ +CREATE TABLE unsigned_table ( + id INTEGER UNSIGNED PRIMARY KEY AUTO_INCREMENT, + value INTEGER UNSIGNED NOT NULL +) CHARACTER SET utf8mb4; \ No newline at end of file diff --git a/collector/benchmarks/diesel/migrations/mysql/2020-02-18-111430_create_pokes/down.sql b/collector/benchmarks/diesel/migrations/mysql/2020-02-18-111430_create_pokes/down.sql new file mode 100644 index 000000000..cdadc2916 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/2020-02-18-111430_create_pokes/down.sql @@ -0,0 +1 @@ +DROP TABLE pokes; diff --git a/collector/benchmarks/diesel/migrations/mysql/2020-02-18-111430_create_pokes/up.sql b/collector/benchmarks/diesel/migrations/mysql/2020-02-18-111430_create_pokes/up.sql new file mode 100644 index 000000000..f685fd318 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/2020-02-18-111430_create_pokes/up.sql @@ -0,0 +1,5 @@ +create table pokes ( + user_id INTEGER PRIMARY KEY NOT NULL REFERENCES users(id), + poke_count INTEGER NOT NULL, + CONSTRAINT pokes_poke_count_check CHECK (poke_count > 0) +); diff --git a/collector/benchmarks/diesel/migrations/postgresql/00000000000000_diesel_initial_setup/down.sql b/collector/benchmarks/diesel/migrations/postgresql/00000000000000_diesel_initial_setup/down.sql new file mode 100644 index 000000000..8fb31a8e5 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/00000000000000_diesel_initial_setup/down.sql @@ -0,0 +1,2 @@ +DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass); +DROP FUNCTION IF EXISTS diesel_set_updated_at(); diff --git a/collector/benchmarks/diesel/migrations/postgresql/00000000000000_diesel_initial_setup/up.sql b/collector/benchmarks/diesel/migrations/postgresql/00000000000000_diesel_initial_setup/up.sql new file mode 100644 index 000000000..d68895b1a --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/00000000000000_diesel_initial_setup/up.sql @@ -0,0 +1,36 @@ +-- This file was automatically created by Diesel to setup helper functions +-- and other internal bookkeeping. This file is safe to edit, any future +-- changes will be added to existing projects as new migrations. + + + + +-- Sets up a trigger for the given table to automatically set a column called +-- `updated_at` whenever the row is modified (unless `updated_at` was included +-- in the modified columns) +-- +-- # Example +-- +-- ```sql +-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW()); +-- +-- SELECT diesel_manage_updated_at('users'); +-- ``` +CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$ +BEGIN + EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s + FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$ +BEGIN + IF ( + NEW IS DISTINCT FROM OLD AND + NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at + ) THEN + NEW.updated_at := current_timestamp; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20151219180527_create_users_and_posts_and_comments/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20151219180527_create_users_and_posts_and_comments/down.sql new file mode 100644 index 000000000..55e7a63b1 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20151219180527_create_users_and_posts_and_comments/down.sql @@ -0,0 +1,3 @@ +DROP TABLE users; +DROP TABLE posts; +DROP TABLE comments; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20151219180527_create_users_and_posts_and_comments/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20151219180527_create_users_and_posts_and_comments/up.sql new file mode 100644 index 000000000..50ec08d3d --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20151219180527_create_users_and_posts_and_comments/up.sql @@ -0,0 +1,18 @@ +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + hair_color VARCHAR +); + +CREATE TABLE posts ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + title VARCHAR NOT NULL, + body TEXT +); + +CREATE TABLE comments ( + id SERIAL PRIMARY KEY, + post_id INTEGER NOT NULL, + text TEXT NOT NULL +); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20160107090901_add_tags_to_posts/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20160107090901_add_tags_to_posts/down.sql new file mode 100644 index 000000000..7b003877c --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20160107090901_add_tags_to_posts/down.sql @@ -0,0 +1 @@ +ALTER TABLE posts DROP COLUMN tags; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20160107090901_add_tags_to_posts/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20160107090901_add_tags_to_posts/up.sql new file mode 100644 index 000000000..12b6189e9 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20160107090901_add_tags_to_posts/up.sql @@ -0,0 +1 @@ +ALTER TABLE posts ADD COLUMN tags varchar[] NOT NULL DEFAULT '{}'; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20160116104628_create_special_posts_and_special_comments/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20160116104628_create_special_posts_and_special_comments/down.sql new file mode 100644 index 000000000..6759f841c --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20160116104628_create_special_posts_and_special_comments/down.sql @@ -0,0 +1,2 @@ +DROP TABLE special_posts; +DROP TABLE special_comments; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20160116104628_create_special_posts_and_special_comments/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20160116104628_create_special_posts_and_special_comments/up.sql new file mode 100644 index 000000000..075e34f9b --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20160116104628_create_special_posts_and_special_comments/up.sql @@ -0,0 +1,10 @@ +CREATE TABLE special_posts ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + title VARCHAR NOT NULL +); + +CREATE TABLE special_comments ( + id SERIAL PRIMARY KEY, + special_post_id INTEGER NOT NULL +); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20160825135747_create_followings/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20160825135747_create_followings/down.sql new file mode 100644 index 000000000..331cb0c9f --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20160825135747_create_followings/down.sql @@ -0,0 +1 @@ +DROP TABLE followings; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20160825135747_create_followings/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20160825135747_create_followings/up.sql new file mode 100644 index 000000000..b68bc1215 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20160825135747_create_followings/up.sql @@ -0,0 +1,6 @@ +CREATE TABLE followings ( + user_id INTEGER NOT NULL, + post_id INTEGER NOT NULL, + email_notifications BOOLEAN NOT NULL DEFAULT 'f', + PRIMARY KEY (user_id, post_id) +); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20161206123630_create_table_custom_schema/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20161206123630_create_table_custom_schema/down.sql new file mode 100644 index 000000000..53533bb59 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20161206123630_create_table_custom_schema/down.sql @@ -0,0 +1 @@ +DROP SCHEMA custom_schema CASCADE; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20161206123630_create_table_custom_schema/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20161206123630_create_table_custom_schema/up.sql new file mode 100644 index 000000000..13df19cfb --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20161206123630_create_table_custom_schema/up.sql @@ -0,0 +1,2 @@ +CREATE SCHEMA custom_schema; +CREATE TABLE custom_schema.users (id SERIAL PRIMARY KEY); diff --git a/collector/benchmarks/diesel/migrations/postgresql/2017-09-26-151545_fix_posts_tags/down.sql b/collector/benchmarks/diesel/migrations/postgresql/2017-09-26-151545_fix_posts_tags/down.sql new file mode 100644 index 000000000..748263eaf --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/2017-09-26-151545_fix_posts_tags/down.sql @@ -0,0 +1 @@ +ALTER TABLE posts ALTER COLUMN tags TYPE varchar[]; diff --git a/collector/benchmarks/diesel/migrations/postgresql/2017-09-26-151545_fix_posts_tags/up.sql b/collector/benchmarks/diesel/migrations/postgresql/2017-09-26-151545_fix_posts_tags/up.sql new file mode 100644 index 000000000..d5671d186 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/2017-09-26-151545_fix_posts_tags/up.sql @@ -0,0 +1 @@ +ALTER TABLE posts ALTER COLUMN tags TYPE text[]; diff --git a/collector/benchmarks/diesel/migrations/postgresql/2017-09-27-135928_remove_citext_table/down.sql b/collector/benchmarks/diesel/migrations/postgresql/2017-09-27-135928_remove_citext_table/down.sql new file mode 100644 index 000000000..bd603fee2 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/2017-09-27-135928_remove_citext_table/down.sql @@ -0,0 +1,2 @@ +CREATE EXTENSION IF NOT EXISTS citext; +CREATE TABLE citext_table (citext_field CITEXT PRIMARY KEY); diff --git a/collector/benchmarks/diesel/migrations/postgresql/2017-09-27-135928_remove_citext_table/up.sql b/collector/benchmarks/diesel/migrations/postgresql/2017-09-27-135928_remove_citext_table/up.sql new file mode 100644 index 000000000..e0e686efe --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/2017-09-27-135928_remove_citext_table/up.sql @@ -0,0 +1 @@ +DROP TABLE citext_table; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170209180355_add_one_off_tables_from_integration_tests/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170209180355_add_one_off_tables_from_integration_tests/down.sql new file mode 100644 index 000000000..db631b629 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170209180355_add_one_off_tables_from_integration_tests/down.sql @@ -0,0 +1,5 @@ +DROP TABLE numbers; +DROP TABLE precision_numbers; +DROP TABLE nullable_doubles; +DROP TABLE users_with_name_pk; +DROP TABLE points; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170209180355_add_one_off_tables_from_integration_tests/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170209180355_add_one_off_tables_from_integration_tests/up.sql new file mode 100644 index 000000000..87278ed34 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170209180355_add_one_off_tables_from_integration_tests/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE numbers (n INTEGER PRIMARY KEY); +CREATE TABLE precision_numbers (n DOUBLE PRECISION NOT NULL PRIMARY KEY); +CREATE TABLE nullable_doubles (id SERIAL PRIMARY KEY, n DOUBLE PRECISION); +CREATE TABLE users_with_name_pk (name VARCHAR PRIMARY KEY); +CREATE TABLE points (x INTEGER NOT NULL, y INTEGER NOT NULL, PRIMARY KEY (x, y)); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170211150830_index_columns_used_in_benchmarks/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170211150830_index_columns_used_in_benchmarks/down.sql new file mode 100644 index 000000000..c19dc97fe --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170211150830_index_columns_used_in_benchmarks/down.sql @@ -0,0 +1,2 @@ +DROP INDEX users_hair_color; +DROP INDEX posts_user_id; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170211150830_index_columns_used_in_benchmarks/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170211150830_index_columns_used_in_benchmarks/up.sql new file mode 100644 index 000000000..b8ecd60c3 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170211150830_index_columns_used_in_benchmarks/up.sql @@ -0,0 +1,2 @@ +CREATE INDEX users_hair_color ON users (hair_color); +CREATE INDEX posts_user_id ON posts (user_id); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170215170122_create_trees/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170215170122_create_trees/down.sql new file mode 100644 index 000000000..1135e76a6 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170215170122_create_trees/down.sql @@ -0,0 +1 @@ +DROP TABLE trees; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170215170122_create_trees/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170215170122_create_trees/up.sql new file mode 100644 index 000000000..c9b7c62a0 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170215170122_create_trees/up.sql @@ -0,0 +1,4 @@ +CREATE TABLE trees ( + id SERIAL PRIMARY KEY, + parent_id INTEGER +); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170219200159_add_foreign_key/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170219200159_add_foreign_key/down.sql new file mode 100644 index 000000000..9dea4d73a --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170219200159_add_foreign_key/down.sql @@ -0,0 +1,2 @@ +DROP TABLE fk_tests; +DROP TABLE fk_inits; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170219200159_add_foreign_key/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170219200159_add_foreign_key/up.sql new file mode 100644 index 000000000..edfa9efc0 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170219200159_add_foreign_key/up.sql @@ -0,0 +1,8 @@ +CREATE TABLE fk_inits ( + id INTEGER PRIMARY KEY +); + +CREATE TABLE fk_tests ( + id INTEGER PRIMARY KEY, + fk_id INTEGER NOT NULL REFERENCES fk_inits(id) +); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170407152306_add_nullable_table/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170407152306_add_nullable_table/down.sql new file mode 100644 index 000000000..ff84418a4 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170407152306_add_nullable_table/down.sql @@ -0,0 +1 @@ +DROP TABLE nullable_table; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170407152306_add_nullable_table/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170407152306_add_nullable_table/up.sql new file mode 100644 index 000000000..f716036ba --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170407152306_add_nullable_table/up.sql @@ -0,0 +1,4 @@ +CREATE TABLE nullable_table ( + id SERIAL PRIMARY KEY, + value INTEGER +); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170603131224_add_likes/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170603131224_add_likes/down.sql new file mode 100644 index 000000000..f7c67fce5 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170603131224_add_likes/down.sql @@ -0,0 +1 @@ +DROP TABLE likes; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170603131224_add_likes/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170603131224_add_likes/up.sql new file mode 100644 index 000000000..cb37d8d14 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170603131224_add_likes/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE likes ( + comment_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + PRIMARY KEY (comment_id, user_id) +); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170717152137_add_ranges/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170717152137_add_ranges/down.sql new file mode 100644 index 000000000..3720ef73c --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170717152137_add_ranges/down.sql @@ -0,0 +1 @@ +DROP TABLE all_the_ranges diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170717152137_add_ranges/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170717152137_add_ranges/up.sql new file mode 100644 index 000000000..655a7ac49 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170717152137_add_ranges/up.sql @@ -0,0 +1,8 @@ +CREATE TABLE all_the_ranges ( + int4 INT4RANGE PRIMARY KEY, + int8 INT8RANGE NOT NULL, + num NUMRANGE NOT NULL, + ts TSRANGE NOT NULL, + tstz TSTZRANGE NOT NULL, + date DATERANGE NOT NULL +) diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170721184144_create_citext_table/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170721184144_create_citext_table/down.sql new file mode 100644 index 000000000..e0e686efe --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170721184144_create_citext_table/down.sql @@ -0,0 +1 @@ +DROP TABLE citext_table; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170721184144_create_citext_table/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170721184144_create_citext_table/up.sql new file mode 100644 index 000000000..bd603fee2 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170721184144_create_citext_table/up.sql @@ -0,0 +1,2 @@ +CREATE EXTENSION IF NOT EXISTS citext; +CREATE TABLE citext_table (citext_field CITEXT PRIMARY KEY); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170804172328_add_foreign_keys/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170804172328_add_foreign_keys/down.sql new file mode 100644 index 000000000..f195a013e --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170804172328_add_foreign_keys/down.sql @@ -0,0 +1,6 @@ +ALTER TABLE posts DROP CONSTRAINT posts_user_id_fkey; +ALTER TABLE comments DROP CONSTRAINT comments_post_id_fkey; +ALTER TABLE followings DROP CONSTRAINT followings_user_id_fkey; +ALTER TABLE followings DROP CONSTRAINT followings_post_id_fkey; +ALTER TABLE likes DROP CONSTRAINT likes_comment_id_fkey; +ALTER TABLE likes DROP CONSTRAINT likes_user_id_fkey; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170804172328_add_foreign_keys/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170804172328_add_foreign_keys/up.sql new file mode 100644 index 000000000..80cc1f8d6 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170804172328_add_foreign_keys/up.sql @@ -0,0 +1,6 @@ +ALTER TABLE posts ADD CONSTRAINT posts_user_id_fkey FOREIGN KEY (user_id) REFERENCES users DEFERRABLE; +ALTER TABLE comments ADD CONSTRAINT comments_post_id_fkey FOREIGN KEY (post_id) REFERENCES posts DEFERRABLE; +ALTER TABLE followings ADD CONSTRAINT followings_user_id_fkey FOREIGN KEY (user_id) REFERENCES users DEFERRABLE; +ALTER TABLE followings ADD CONSTRAINT followings_post_id_fkey FOREIGN KEY (post_id) REFERENCES posts DEFERRABLE; +ALTER TABLE likes ADD CONSTRAINT likes_comment_id_fkey FOREIGN KEY (comment_id) REFERENCES comments DEFERRABLE; +ALTER TABLE likes ADD CONSTRAINT likes_user_id_fkey FOREIGN KEY (user_id) REFERENCES users DEFERRABLE; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql new file mode 100644 index 000000000..36f05ade1 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql @@ -0,0 +1,7 @@ +DROP TABLE self_referential_fk; +DROP TABLE fk_doesnt_reference_pk; +ALTER TABLE posts DROP CONSTRAINT title_is_unique; +DROP TABLE composite_fk; +DROP TABLE multiple_fks_to_same_table; +DROP TABLE cyclic_fk_2 CASCADE; +DROP TABLE cyclic_fk_1 CASCADE; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql new file mode 100644 index 000000000..16ffe227d --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql @@ -0,0 +1,37 @@ +CREATE TABLE self_referential_fk ( + id SERIAL PRIMARY KEY, + parent_id INTEGER NOT NULL +); + +ALTER TABLE self_referential_fk ADD CONSTRAINT self_referential_fk_parent_id_fk FOREIGN KEY (parent_id) REFERENCES self_referential_fk; + +ALTER TABLE posts ADD CONSTRAINT title_is_unique UNIQUE (title); +CREATE TABLE fk_doesnt_reference_pk ( + id SERIAL PRIMARY KEY, + random TEXT REFERENCES posts (title) +); + +CREATE TABLE composite_fk ( + id SERIAL PRIMARY KEY, + post_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + FOREIGN KEY (user_id, post_id) REFERENCES followings +); + +CREATE TABLE multiple_fks_to_same_table ( + id SERIAL PRIMARY KEY, + post_id_1 INTEGER REFERENCES posts, + post_id_2 INTEGER REFERENCES posts +); + +CREATE TABLE cyclic_fk_1 ( + id SERIAL PRIMARY KEY, + cyclic_fk_2_id INTEGER +); + +CREATE TABLE cyclic_fk_2 ( + id SERIAL PRIMARY KEY, + cyclic_fk_1_id INTEGER REFERENCES cyclic_fk_1 +); + +ALTER TABLE cyclic_fk_1 ADD CONSTRAINT cyclic_fk_1_cyclic_fk_2_id_fk FOREIGN KEY (cyclic_fk_2_id) REFERENCES cyclic_fk_2; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170816164352_add_keywords_table/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170816164352_add_keywords_table/down.sql new file mode 100644 index 000000000..20296cf53 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170816164352_add_keywords_table/down.sql @@ -0,0 +1 @@ +DROP TABLE with_keywords; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170816164352_add_keywords_table/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170816164352_add_keywords_table/up.sql new file mode 100644 index 000000000..3574ab861 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170816164352_add_keywords_table/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE with_keywords ( + fn INTEGER PRIMARY KEY, + let INTEGER NOT NULL, + extern INTEGER NOT NULL +); diff --git a/collector/benchmarks/diesel/migrations/postgresql/2020-02-18-111430_create_pokes/down.sql b/collector/benchmarks/diesel/migrations/postgresql/2020-02-18-111430_create_pokes/down.sql new file mode 100644 index 000000000..cdadc2916 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/2020-02-18-111430_create_pokes/down.sql @@ -0,0 +1 @@ +DROP TABLE pokes; diff --git a/collector/benchmarks/diesel/migrations/postgresql/2020-02-18-111430_create_pokes/up.sql b/collector/benchmarks/diesel/migrations/postgresql/2020-02-18-111430_create_pokes/up.sql new file mode 100644 index 000000000..f685fd318 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/2020-02-18-111430_create_pokes/up.sql @@ -0,0 +1,5 @@ +create table pokes ( + user_id INTEGER PRIMARY KEY NOT NULL REFERENCES users(id), + poke_count INTEGER NOT NULL, + CONSTRAINT pokes_poke_count_check CHECK (poke_count > 0) +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20151219180527_create_users_and_posts_and_comments/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20151219180527_create_users_and_posts_and_comments/down.sql new file mode 100644 index 000000000..55e7a63b1 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20151219180527_create_users_and_posts_and_comments/down.sql @@ -0,0 +1,3 @@ +DROP TABLE users; +DROP TABLE posts; +DROP TABLE comments; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20151219180527_create_users_and_posts_and_comments/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20151219180527_create_users_and_posts_and_comments/up.sql new file mode 100644 index 000000000..e26da6e1c --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20151219180527_create_users_and_posts_and_comments/up.sql @@ -0,0 +1,18 @@ +CREATE TABLE users ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + name VARCHAR NOT NULL, + hair_color VARCHAR +); + +CREATE TABLE posts ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + user_id INTEGER NOT NULL, + title VARCHAR NOT NULL, + body TEXT +); + +CREATE TABLE comments ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + post_id INTEGER NOT NULL, + text TEXT NOT NULL +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20160116104628_create_special_posts_and_special_comments/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20160116104628_create_special_posts_and_special_comments/down.sql new file mode 100644 index 000000000..6759f841c --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20160116104628_create_special_posts_and_special_comments/down.sql @@ -0,0 +1,2 @@ +DROP TABLE special_posts; +DROP TABLE special_comments; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20160116104628_create_special_posts_and_special_comments/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20160116104628_create_special_posts_and_special_comments/up.sql new file mode 100644 index 000000000..9b860a13f --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20160116104628_create_special_posts_and_special_comments/up.sql @@ -0,0 +1,10 @@ +CREATE TABLE special_posts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + title VARCHAR NOT NULL +); + +CREATE TABLE special_comments ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + special_post_id INTEGER NOT NULL +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20160416143735_infer_all_the_types/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20160416143735_infer_all_the_types/down.sql new file mode 100644 index 000000000..61e4546a7 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20160416143735_infer_all_the_types/down.sql @@ -0,0 +1,4 @@ +DROP TABLE infer_all_the_ints; +DROP TABLE infer_all_the_bools; +DROP TABLE infer_all_the_strings; +DROP TABLE infer_all_the_floats; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20160416143735_infer_all_the_types/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20160416143735_infer_all_the_types/up.sql new file mode 100644 index 000000000..03e7e9864 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20160416143735_infer_all_the_types/up.sql @@ -0,0 +1,46 @@ +-- Semi-exhaustive checking of many possible invocations of supported types +-- listed at https://www.sqlite.org/datatype3.html to ensure it compiles +CREATE TABLE infer_all_the_ints ( + col1 INTEGER PRIMARY KEY NOT NULL, + col2 INT NOT NULL, + col3 INTEGER NOT NULL, + col4 LOL_WHAT_EVEN_IS_THIS_TYPE_CAN_I_HAVE_A_HINT NOT NULL, + col5 SMALLINT NOT NULL, + col6 SMALLINT(2) NOT NULL, + col7 SMALL INT NOT NULL, + col8 BIGINT NOT NULL, + col9 BIGINT(4) NOT NULL, + col10 BIG INT NOT NULL, + col11 INT2 NOT NULL, + col12 INT4 NOT NULL, + col13 INT8 NOT NULL +); + +CREATE TABLE infer_all_the_bools ( + col1 TINYINT(1) PRIMARY KEY NOT NULL, + col2 TINYINT NOT NULL, + col3 TINY INT NOT NULL, + col4 BOOLEAN NOT NULL +); + +CREATE TABLE infer_all_the_strings ( + col1 CHARACTER(20) PRIMARY KEY NOT NULL, + col2 VARCHAR(255) NOT NULL, + col3 VARYING CHARACTER(255) NOT NULL, + col4 NCHAR(55) NOT NULL, + col5 NATIVE CHARACTER(70) NOT NULL, + col6 NVARCHAR(100) NOT NULL, + col7 TEXT NOT NULL, + col8 CLOB NOT NULL, + col9 BLOB NOT NULL, + col10 NOT NULL +); + +CREATE TABLE infer_all_the_floats ( + col1 REAL PRIMARY KEY NOT NULL, + col2 FLOAT NOT NULL, + col3 DOUBLE NOT NULL, + col4 DOUBLE PRECISION NOT NULL, + col5 NUMERIC NOT NULL, + col6 DECIMAL(10, 5) NOT NULL +) diff --git a/collector/benchmarks/diesel/migrations/sqlite/20160825135747_create_followings/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20160825135747_create_followings/down.sql new file mode 100644 index 000000000..331cb0c9f --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20160825135747_create_followings/down.sql @@ -0,0 +1 @@ +DROP TABLE followings; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20160825135747_create_followings/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20160825135747_create_followings/up.sql new file mode 100644 index 000000000..d1ec723ba --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20160825135747_create_followings/up.sql @@ -0,0 +1,6 @@ +CREATE TABLE followings ( + user_id INTEGER NOT NULL, + post_id INTEGER NOT NULL, + email_notifications BOOLEAN NOT NULL DEFAULT 0, + PRIMARY KEY (user_id, post_id) +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170209180355_add_one_off_tables_from_integration_tests/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170209180355_add_one_off_tables_from_integration_tests/down.sql new file mode 100644 index 000000000..db631b629 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170209180355_add_one_off_tables_from_integration_tests/down.sql @@ -0,0 +1,5 @@ +DROP TABLE numbers; +DROP TABLE precision_numbers; +DROP TABLE nullable_doubles; +DROP TABLE users_with_name_pk; +DROP TABLE points; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170209180355_add_one_off_tables_from_integration_tests/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170209180355_add_one_off_tables_from_integration_tests/up.sql new file mode 100644 index 000000000..9adff778a --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170209180355_add_one_off_tables_from_integration_tests/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE numbers (n INTEGER PRIMARY KEY); +CREATE TABLE precision_numbers (n DOUBLE PRECISION NOT NULL PRIMARY KEY); +CREATE TABLE nullable_doubles (id INTEGER PRIMARY KEY AUTOINCREMENT, n DOUBLE PRECISION); +CREATE TABLE users_with_name_pk (name VARCHAR PRIMARY KEY); +CREATE TABLE points (x INTEGER NOT NULL, y INTEGER NOT NULL, PRIMARY KEY (x, y)); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170211150830_index_columns_used_in_benchmarks/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170211150830_index_columns_used_in_benchmarks/down.sql new file mode 100644 index 000000000..c19dc97fe --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170211150830_index_columns_used_in_benchmarks/down.sql @@ -0,0 +1,2 @@ +DROP INDEX users_hair_color; +DROP INDEX posts_user_id; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170211150830_index_columns_used_in_benchmarks/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170211150830_index_columns_used_in_benchmarks/up.sql new file mode 100644 index 000000000..b8ecd60c3 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170211150830_index_columns_used_in_benchmarks/up.sql @@ -0,0 +1,2 @@ +CREATE INDEX users_hair_color ON users (hair_color); +CREATE INDEX posts_user_id ON posts (user_id); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170215170122_create_trees/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170215170122_create_trees/down.sql new file mode 100644 index 000000000..1135e76a6 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170215170122_create_trees/down.sql @@ -0,0 +1 @@ +DROP TABLE trees; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170215170122_create_trees/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170215170122_create_trees/up.sql new file mode 100644 index 000000000..1b07e5cfe --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170215170122_create_trees/up.sql @@ -0,0 +1,4 @@ +CREATE TABLE trees ( + id INTEGER PRIMARY KEY NOT NULL, + parent_id INTEGER +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170219200159_add_foreign_key/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170219200159_add_foreign_key/down.sql new file mode 100644 index 000000000..9dea4d73a --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170219200159_add_foreign_key/down.sql @@ -0,0 +1,2 @@ +DROP TABLE fk_tests; +DROP TABLE fk_inits; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170219200159_add_foreign_key/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170219200159_add_foreign_key/up.sql new file mode 100644 index 000000000..b6ebedbc2 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170219200159_add_foreign_key/up.sql @@ -0,0 +1,9 @@ +CREATE TABLE fk_inits ( + id INTEGER PRIMARY KEY +); + +CREATE TABLE fk_tests ( + id INTEGER PRIMARY KEY, + fk_id INTEGER NOT NULL, + FOREIGN KEY(fk_id) REFERENCES fk_inits(id) +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170407152306_add_nullable_table/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170407152306_add_nullable_table/down.sql new file mode 100644 index 000000000..ff84418a4 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170407152306_add_nullable_table/down.sql @@ -0,0 +1 @@ +DROP TABLE nullable_table; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170407152306_add_nullable_table/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170407152306_add_nullable_table/up.sql new file mode 100644 index 000000000..2ccb6af34 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170407152306_add_nullable_table/up.sql @@ -0,0 +1,4 @@ +CREATE TABLE nullable_table ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + value INTEGER +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170513174919_infer_all_the_datetime_types/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170513174919_infer_all_the_datetime_types/down.sql new file mode 100644 index 000000000..aceb295d2 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170513174919_infer_all_the_datetime_types/down.sql @@ -0,0 +1 @@ +DROP TABLE infer_all_the_datetime_types; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170513174919_infer_all_the_datetime_types/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170513174919_infer_all_the_datetime_types/up.sql new file mode 100644 index 000000000..63b012db9 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170513174919_infer_all_the_datetime_types/up.sql @@ -0,0 +1,6 @@ +CREATE TABLE infer_all_the_datetime_types ( + dt DATETIME PRIMARY KEY NOT NULL, + date DATE NOT NULL, + time TIME NOT NULL, + timestamp TIMESTAMP NOT NULL +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170603131224_add_likes/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170603131224_add_likes/down.sql new file mode 100644 index 000000000..f7c67fce5 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170603131224_add_likes/down.sql @@ -0,0 +1 @@ +DROP TABLE likes; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170603131224_add_likes/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170603131224_add_likes/up.sql new file mode 100644 index 000000000..cb37d8d14 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170603131224_add_likes/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE likes ( + comment_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + PRIMARY KEY (comment_id, user_id) +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170804172328_add_foreign_keys/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170804172328_add_foreign_keys/down.sql new file mode 100644 index 000000000..4526e15ec --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170804172328_add_foreign_keys/down.sql @@ -0,0 +1,33 @@ +-- SQLite has no useful ALTER TABLE statement for this, so we must drop and +-- re-create them. Table definitions came from `SELECT sql FROM sqlite_master` + +DROP TABLE likes; +CREATE TABLE likes ( + comment_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + PRIMARY KEY (comment_id, user_id) +); + +DROP TABLE followings; +CREATE TABLE followings ( + user_id INTEGER NOT NULL, + post_id INTEGER NOT NULL, + email_notifications BOOLEAN NOT NULL DEFAULT 0, + PRIMARY KEY (user_id, post_id) +); + +DROP TABLE comments; +CREATE TABLE comments ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + post_id INTEGER NOT NULL, + text TEXT NOT NULL +); + +DROP TABLE posts; +CREATE TABLE posts ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + user_id INTEGER NOT NULL, + title VARCHAR NOT NULL, + body TEXT +); +CREATE INDEX posts_user_id ON posts (user_id); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170804172328_add_foreign_keys/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170804172328_add_foreign_keys/up.sql new file mode 100644 index 000000000..0b8329578 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170804172328_add_foreign_keys/up.sql @@ -0,0 +1,39 @@ +-- SQLite has no useful ALTER TABLE statement for this, so we must drop and +-- re-create them. Table definitions came from `SELECT sql FROM sqlite_master` + +DROP TABLE posts; +CREATE TABLE posts ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + user_id INTEGER NOT NULL, + title VARCHAR NOT NULL, + body TEXT, + FOREIGN KEY (user_id) REFERENCES users (id) +); +CREATE INDEX posts_user_id ON posts (user_id); + +DROP TABLE comments; +CREATE TABLE comments ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + post_id INTEGER NOT NULL, + text TEXT NOT NULL, + FOREIGN KEY (post_id) REFERENCES posts (id) +); + +DROP TABLE followings; +CREATE TABLE followings ( + user_id INTEGER NOT NULL, + post_id INTEGER NOT NULL, + email_notifications BOOLEAN NOT NULL DEFAULT 0, + PRIMARY KEY (user_id, post_id), + FOREIGN KEY (user_id) REFERENCES users (id), + FOREIGN KEY (post_id) REFERENCES posts (id) +); + +DROP TABLE likes; +CREATE TABLE likes ( + comment_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + PRIMARY KEY (comment_id, user_id), + FOREIGN KEY (user_id) REFERENCES users (id), + FOREIGN KEY (comment_id) REFERENCES comments (id) +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql new file mode 100644 index 000000000..0813f33ee --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql @@ -0,0 +1,7 @@ +DROP TABLE self_referential_fk; +DROP INDEX posts_title_is_unique; +DROP TABLE fk_doesnt_reference_pk; +DROP TABLE composite_fk; +DROP TABLE multiple_fks_to_same_table; +DROP TABLE cyclic_fk_2; +DROP TABLE cyclic_fk_1; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql new file mode 100644 index 000000000..ad39d3323 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql @@ -0,0 +1,39 @@ +CREATE TABLE self_referential_fk ( + id INTEGER PRIMARY KEY, + parent_id INTEGER NOT NULL, + FOREIGN KEY (parent_id) REFERENCES self_referential_fk (id) +); + +CREATE UNIQUE INDEX posts_title_is_unique ON posts (title); +CREATE TABLE fk_doesnt_reference_pk ( + id INTEGER PRIMARY KEY, + random TEXT, + FOREIGN KEY (random) REFERENCES posts (title) +); + +CREATE TABLE composite_fk ( + id INTEGER PRIMARY KEY, + post_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + FOREIGN KEY (user_id, post_id) REFERENCES followings (user_id, post_id) +); + +CREATE TABLE multiple_fks_to_same_table ( + id INTEGER PRIMARY KEY, + post_id_1, + post_id_2, + FOREIGN KEY (post_id_1) REFERENCES posts (id), + FOREIGN KEY (post_id_2) REFERENCES posts (id) +); + +CREATE TABLE cyclic_fk_1 ( + id INTEGER PRIMARY KEY, + cyclic_fk_2_id, + FOREIGN KEY (cyclic_fk_2_id) REFERENCES cyclic_fk_2 (id) +); + +CREATE TABLE cyclic_fk_2 ( + id INTEGER PRIMARY KEY, + cyclic_fk_1_id, + FOREIGN KEY (cyclic_fk_1_id) REFERENCES cyclic_fk_1 (id) +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170816164356_add_keywords_table/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170816164356_add_keywords_table/down.sql new file mode 100644 index 000000000..20296cf53 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170816164356_add_keywords_table/down.sql @@ -0,0 +1 @@ +DROP TABLE with_keywords; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170816164356_add_keywords_table/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170816164356_add_keywords_table/up.sql new file mode 100644 index 000000000..fec19b939 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170816164356_add_keywords_table/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE with_keywords ( + fn INTEGER NOT NULL PRIMARY KEY, + let INTEGER NOT NULL, + extern INTEGER NOT NULL +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/2020-02-18-111430_create_pokes/down.sql b/collector/benchmarks/diesel/migrations/sqlite/2020-02-18-111430_create_pokes/down.sql new file mode 100644 index 000000000..cdadc2916 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/2020-02-18-111430_create_pokes/down.sql @@ -0,0 +1 @@ +DROP TABLE pokes; diff --git a/collector/benchmarks/diesel/migrations/sqlite/2020-02-18-111430_create_pokes/up.sql b/collector/benchmarks/diesel/migrations/sqlite/2020-02-18-111430_create_pokes/up.sql new file mode 100644 index 000000000..f685fd318 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/2020-02-18-111430_create_pokes/up.sql @@ -0,0 +1,5 @@ +create table pokes ( + user_id INTEGER PRIMARY KEY NOT NULL REFERENCES users(id), + poke_count INTEGER NOT NULL, + CONSTRAINT pokes_poke_count_check CHECK (poke_count > 0) +); diff --git a/collector/benchmarks/diesel/perf-config.json b/collector/benchmarks/diesel/perf-config.json new file mode 100644 index 000000000..efc44675e --- /dev/null +++ b/collector/benchmarks/diesel/perf-config.json @@ -0,0 +1,4 @@ +{ + "cargo_toml": "diesel/Cargo.toml", + "touch_file": "diesel/src/lib.rs" +} From ac9a2bdb54dfc198115efe627a33c618637096c2 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Tue, 15 Dec 2020 09:20:09 +0100 Subject: [PATCH 2/2] Decrease to 32-columns feature for diesel to speed up the benchmark --- collector/benchmarks/diesel/diesel/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collector/benchmarks/diesel/diesel/Cargo.toml b/collector/benchmarks/diesel/diesel/Cargo.toml index c2a75950b..4fec150f3 100644 --- a/collector/benchmarks/diesel/diesel/Cargo.toml +++ b/collector/benchmarks/diesel/diesel/Cargo.toml @@ -44,7 +44,7 @@ ipnetwork = ">=0.12.2, <0.18.0" quickcheck = "0.9" [features] -default = ["128-column-tables"] +default = ["32-column-tables"] extras = ["chrono", "serde_json", "uuid", "network-address", "numeric", "r2d2"] unstable = ["diesel_derives/nightly"] large-tables = ["32-column-tables"]